(*********************************************************************** These are includes needed for wireguard.cv. ***********************************************************************) (*********************************************************************** The following is default.cvl shipped with CryptoVerif ***********************************************************************) 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). const bottom:bitstringbot. (* We define the equivalence used internally by the command "move array" *) def move_array_internal_macro(T) { param N, NX, Neq. equiv(move_array(T)) foreach i<=N do X <-R T; (foreach iX <= NX do OX() := return(X) | foreach ieq <= Neq do Oeq(X':T) := return(X' = X)) <=(#Oeq * Pcoll1rand(T))=> [manual] foreach i<=N do (foreach iX <= NX do OX() := find[unique] j<=NX suchthat defined(Y[j]) then return(Y[j]) else Y <-R T; return(Y) | foreach ieq <= Neq do Oeq(X':T) := find[unique] j<=NX suchthat defined(Y[j]) then return(X' = Y[j]) else return(false)). } (******************************** 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. param N. equiv(keygen(kgen)) foreach i <= N do r <-R keyseed; Okey() := return(kgen(r)) <=(0)=> foreach i <= N do 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, N2. 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)) foreach i2 <= N2 do k <-R key; foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) := return(enc_r(x, k, r)) <=(N2 * Penc(time + (N2-1)*(N*time(enc_r, maxlength(x)) + N*time(Z, maxlength(x))), N, maxlength(x)))=> foreach i2 <= N2 do 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 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)) foreach i2 <= N2 do k <-R key; foreach i <= N do r <-R enc_seed; Oenc(x:cleartext) := return(enc_r(x, k, r)) <=(N2 * Penc(time + (N2-1)*(N*time(enc_r, maxlength(x)) + N*time(Z, maxlength(x))), N, maxlength(x)))=> foreach i2 <= N2 do 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)) foreach i2 <= N2 do 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))) <=(N2 * Pencctxt(time + (N2-1)*(N*time(enc_r, maxlength(x)) + N3*time(dec,maxlength(y))), N, N3, maxlength(x), maxlength(y)))=> [computational] foreach i2 <= N2 do 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)) foreach i2 <= N2 do 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)) <=(N2 * Pencctxt(time + (N2-1)*(N*time(enc_r, maxlength(x)) + N3*time(dec,maxlength(y))), N, N3, maxlength(x), maxlength(y)))=> [manual,computational] foreach i2 <= N2 do 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). } (* 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)) foreach i2 <= N2 do k <-R key; foreach i <= N do r <-R enc_seed; Oenc(x:cleartext, d: add_data) := return(enc_r(x, d, k, r)) <=(N2 * Penc(time + (N2-1)*(N*time(enc_r, maxlength(x), maxlength(d)) + N*time(Z, maxlength(x))), N, maxlength(x)))=> foreach i2 <= N2 do 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)) foreach i2 <= N2 do 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))) <=(N2 * Pencctxt(time + (N2-1)*(N*time(enc_r, maxlength(x), maxlength(d)) + N3*time(dec,maxlength(y),maxlength(c_d))), N, N3, maxlength(x), maxlength(y), maxlength(d), maxlength(c_d)))=> [computational] foreach i2 <= N2 do 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)) foreach i2 <= N2 do 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)) <=(N2 * Pencctxt(time + (N2-1)*(N*time(enc_r, maxlength(x), maxlength(d)) + N3*time(dec,maxlength(y),maxlength(c_d))), N, N3, maxlength(x), maxlength(y), maxlength(d), maxlength(c_d)))=> [manual,computational] foreach i2 <= N2 do 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)) foreach i2 <= N2 do k <-R key; foreach i <= N do Oenc(x:cleartext, d: add_data, n: nonce) := return(enc(x, d, k, n)) <=(N2 * Penc(time + (N2-1)*(N*time(enc, maxlength(x), maxlength(d), maxlength(n)) + N*time(Z, maxlength(x))), N, maxlength(x)))=> foreach i2 <= N2 do 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)) foreach i2 <= N2 do 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))) <=(N2 * Pencctxt(time + (N2-1)*(N*time(enc, maxlength(x), maxlength(d), maxlength(n)) + N3*time(dec,maxlength(y),maxlength(c_d),maxlength(c_n))), N, N3, maxlength(x), maxlength(y), maxlength(d), maxlength(c_d)))=> [computational] foreach i2 <= N2 do 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)) foreach i2 <= N2 do 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)) <=(N2 * Pencctxt(time + (N2-1)*(N*time(enc, maxlength(x), maxlength(d), maxlength(n)) + N3*time(dec,maxlength(y),maxlength(c_d),maxlength(c_n))), N, N3, maxlength(x), maxlength(y), maxlength(d), maxlength(c_d)))=> [manual,computational] foreach i2 <= N2 do 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-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)) foreach i2 <= N2 do 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))) <=(N2 * Penc(time + (N2-1)*(N*time(enc_r, maxlength(x)) + N*time(Z, maxlength(x)) + N3*time(dec,maxlength(y))), N, 0, N3, maxlength(x), maxlength(y)))=> foreach i2 <= N2 do 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)) foreach i2 <= N2 do 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))) <=(N2 * Penc(time + (N2-1)*((N+N'+N'')*time(enc_r, max(maxlength(x),maxlength(x'),maxlength(x''))) + (N+N'+N'')*time(Z, maxlength(x)) + N3*time(dec,maxlength(y))), N, N'+N'', N3, max(maxlength(x),maxlength(x'),maxlength(x'')), maxlength(y)))=> [manual] foreach i2 <= N2 do 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)) foreach i2 <= N2 do 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))) <=(N2 * Pencptxt(time + (N2-1)*(N*time(enc_r, maxlength(x)) + N3*time(dec,maxlength(y))), N, N3, 0, maxlength(x), maxlength(y)))=> [computational] foreach i2 <= N2 do 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)) foreach i2 <= N2 do 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)) <=(N2 * Pencptxt(time + (N2-1)*(N*time(enc_r, maxlength(x)) + (N3+N3'+N3'')*time(dec,max(maxlength(y), maxlength(y'),maxlength(y'')))), N, N3, N3'+N3'', maxlength(x), max(maxlength(y),maxlength(y'),maxlength(y''))))=> [manual,computational] foreach i2 <= N2 do 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)) foreach i2 <= N2 do 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))) <=(N2 * Penc(time + (N2-1)*(N*time(enc_r, maxlength(x)) + N*time(Z, maxlength(x)) + N3*time(dec,maxlength(y))), N, 0, N3, maxlength(x), maxlength(y)))=> foreach i2 <= N2 do 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)) foreach i2 <= N2 do 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))) <=(N2 * Penc(time + (N2-1)*(N*time(enc_r, maxlength(x)) + N*time(Z, maxlength(x)) + N3*time(dec,maxlength(y))), N, 0, N3, maxlength(x), maxlength(y)))=> foreach i2 <= N2 do 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)) foreach i2 <= N2 do 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))) <=(N2 * Penc(time + (N2-1)*((N+N')*time(enc_r, max(maxlength(x),maxlength(x'))) + (N+N')*time(Z, maxlength(x)) + N3*time(dec,maxlength(y))), N, N', N3, max(maxlength(x),maxlength(x')), maxlength(y)))=> [manual] foreach i2 <= N2 do 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)) foreach i2 <= N2 do 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))) <=(N2 * Pencptxt(time + (N2-1)*(N*time(enc_r, maxlength(x)) + N3*time(dec,maxlength(y))), N, N3, 0, maxlength(x), maxlength(y)))=> [computational] foreach i2 <= N2 do 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)) foreach i2 <= N2 do 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))) <=(N2 * Pencptxt(time + (N2-1)*(N*time(enc_r, maxlength(x)) + N3*time(dec,maxlength(y))), N, N3, 0, maxlength(x), maxlength(y)))=> [computational] foreach i2 <= N2 do 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)) foreach i2 <= N2 do 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)) <=(N2 * Pencptxt(time + (N2-1)*(N*time(enc_r, maxlength(x)) + (N3+N3')*time(dec,max(maxlength(y),maxlength(y')))), N, N3, N3', maxlength(x), max(maxlength(y),maxlength(y'))))=> [manual,computational] foreach i2 <= N2 do 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)) foreach ik <= Nk do k <-R key; ( foreach ie <= Ne do Oenc(x:blocksize) := return(enc(x, k)) | foreach id <= Nd do Odec(m:blocksize) := return(dec(m, k)) | foreach iecoll <= Necoll do Oenccoll(x':blocksize, re': blocksize) := return(enc(x', k) = re') | foreach idcoll <= Ndcoll do Odeccoll(m':blocksize, rd': blocksize) := return(dec(m', k) = rd')) <=(Nk * (Penc(time + (Nk-1)*((Ne+Necoll)*time(enc) + (Nd+Ndcoll)*time(dec)), Ne+Necoll, Nd+Ndcoll) + (Ne+Nd)*(Ne+Nd-1) * Pcoll2rand(blocksize) + (Necoll + Ndcoll) * Pcoll1rand(blocksize)))=> foreach ik <= Nk do ( foreach ie <= Ne do Oenc(x:blocksize) := find[unique] je<=Ne suchthat defined(x[je],re[je]) && x = x[je] then return(re[je]) orfind jd<=Nd suchthat defined(rd[jd],m[jd]) && x = rd[jd] then return(m[jd]) else re <-R blocksize; return(re) | foreach id <= Nd do Odec(m:blocksize) := find[unique] je<=Ne suchthat defined(x[je],re[je]) && m = re[je] then return(x[je]) orfind jd<=Nd suchthat defined(rd[jd],m[jd]) && m = m[jd] then return(rd[jd]) else rd <-R blocksize; return(rd) | foreach iecoll <= Necoll do Oenccoll(x':blocksize, re': blocksize) := find[unique] je<=Ne suchthat defined(x[je],re[je]) && x' = x[je] then return(re[je] = re') orfind jd<=Nd suchthat defined(rd[jd],m[jd]) && x' = rd[jd] then return(m[jd] = re') else return(false) | foreach idcoll <= Ndcoll do Odeccoll(m':blocksize, rd': blocksize) := find[unique] je<=Ne suchthat defined(x[je],re[je]) && m' = re[je] then return(x[je] = rd') orfind jd<=Nd suchthat defined(rd[jd],m[jd]) && m' = m[jd] then return(rd[jd] = rd') else return(false)). } (* 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 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(prp(enc)) foreach ik <= Nk do k <-R key; (foreach i <= N do Oenc(x:blocksize) := return(enc(x, k)) | foreach icoll <= Ncoll do Oenccoll(x':blocksize, re':blocksize) := return(enc(x', k) = re')) <=(Nk * (Penc(time + (Nk-1)*((N+Ncoll)*time(enc)), N+Ncoll) + N * (N-1) * Pcoll2rand(blocksize) + Ncoll * Pcoll1rand(blocksize)))=> foreach ik <= Nk do (foreach i <= N do Oenc(x:blocksize) := find[unique] j<=N suchthat defined(x[j],re[j]) && x = x[j] then return(re[j]) else re <-R blocksize; return(re) | foreach icoll <= Ncoll do Oenccoll(x':blocksize, re':blocksize) := find[unique] j<=N suchthat defined(x[j],re[j]) && x' = x[j] then return(re[j] = re') else return(false)). } (*************************************** 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)) foreach i3 <= N3 do k <-R mkey;( foreach i <= N do Omac(x: macinput) := return(mac(x, k)) | foreach i2 <= N2 do Ocheck(m: macinput, ma: macres) := return(check(m, k, ma))) <=(N3 * Pmac(time + (N3-1)*(N*time(mac,maxlength(x)) + N2*time(check,maxlength(m),maxlength(ma))), N, N2, 0, max(maxlength(x), maxlength(m))))=> [computational] foreach i3 <= N3 do 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)) foreach i3 <= N3 do k <-R mkey;( foreach i <= N do Omac(x: macinput) [useful_change] := return(mac(x, k)) | foreach i2 <= N2 do Ocheck(m: macinput, ma: macres) [useful_change] := return(check(m, k, ma)) | Ocorrupt() [10] := return(k)) <=(N3 * Pmac(time + (N3-1)*(N*time(mac,maxlength(x)) + N2*time(check,maxlength(m),maxlength(ma))), N, N2, 0, max(maxlength(x), maxlength(m))))=> [manual,computational] foreach i3 <= N3 do 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)) foreach i3 <= N3 do k <-R mkey;( foreach i <= N do Omac(x: macinput) [useful_change] := 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)) <=(N3 * Pmac(time + (N3-1)*(N*time(mac,maxlength(x)) + (N2+N2')*time(check,max(maxlength(m),maxlength(m')),max(maxlength(ma), maxlength(ma')))), N, N2, N2', max(maxlength(x), max(maxlength(m),maxlength(m')))))=> [manual,computational] foreach i3 <= N3 do 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)) foreach i3 <= N3 do 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) := return(check(m, k, ma))) <=(N3 * Pmac(time + (N3-1)*(N*time(mac_r,maxlength(x)) + N2*time(check,maxlength(m),maxlength(ma))), N, N2, 0, max(maxlength(x), maxlength(m))))=> [computational] foreach i3 <= N3 do 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)) foreach i3 <= N3 do 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)) <=(N3 * Pmac(time + (N3-1)*(N*time(mac_r,maxlength(x)) + N2*time(check,maxlength(m),maxlength(ma))), N, N2, 0, max(maxlength(x), maxlength(m))))=> [manual,computational] foreach i3 <= N3 do 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)) foreach i3 <= N3 do 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)) <=(N3 * Pmac(time + (N3-1)*(N*time(mac_r,maxlength(x)) + (N2+N2')*time(check,max(maxlength(m),maxlength(m')),max(maxlength(ma),maxlength(ma')))), N, N2, N2', max(maxlength(x), maxlength(m),maxlength(m'))))=> [manual,computational] foreach i3 <= N3 do 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)) foreach i3 <= N3 do 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) := return(check(m, k, ma))) <=(N3 * Pmac(time + (N3-1)*(N*time(mac_r,maxlength(x)) + N2*time(check,maxlength(m),maxlength(ma))), N, N2, 0, max(maxlength(x), maxlength(m))))=> [computational] foreach i3 <= N3 do 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)) foreach i3 <= N3 do 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)) <=(N3 * Pmac(time + (N3-1)*(N*time(mac_r,maxlength(x)) + N2*time(check,maxlength(m),maxlength(ma))), N, N2, 0, max(maxlength(x), maxlength(m))))=> [manual,computational] foreach i3 <= N3 do 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)) foreach i3 <= N3 do 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)) <=(N3 * Pmac(time + (N3-1)*(N*time(mac_r,maxlength(x)) + (N2+N2')*time(check,max(maxlength(m),maxlength(m')),max(maxlength(ma),maxlength(ma')))), N, N2, N2', max(maxlength(x), maxlength(m), maxlength(m'))))=> [manual,computational] foreach i3 <= N3 do 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). 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) := find j <= N suchthat defined(m1[j],x1[j]) && m = m1[j] then return(injbot(x1[j])) else find j <= N4 suchthat defined(m2[j],y[j],x[j]) && y[j] = pkgen2(r) && m = m2[j] then return(injbot(x[j])) else return(dec2(m, skgen2(r))) | foreach i <= N do r1 <-R enc_seed; Oenc(x1:cleartext) := m1:ciphertext <- enc_r2(Z(x1), pkgen2(r), r1); 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:ciphertext <- enc_r2(Z(x), y, r2); 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. *) equiv(ind_cca2_partial(enc)) foreach i3 <= N3 do 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))) <=((N3 * N) * Penc(time + (N-1) * time(enc_r, maxlength(x1)) + (N3-1)*(time(pkgen) + time(skgen) + N2 * time(dec, maxlength(m)) + N * time(enc_r, maxlength(x1))), N2))=> [manual] foreach i3 <= N3 do r <-R keyseed; ( Opk() := return(pkgen(r)) | foreach i2 <= N2 do Odec(m:ciphertext) := find j <= N suchthat defined(m1[j],x1[j]) && m = m1[j] then return(injbot(x1[j])) else return(dec(m, skgen(r))) | foreach i <= N do r1 <-R enc_seed; Oenc(x1:cleartext) := m1:ciphertext <- enc_r2(Z(x1), pkgen(r), r1); 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)) foreach i3 <= N3 do 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)) <=(N3 * Psign(time + (N-1) * time(check, maxlength(m1), 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(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)) foreach i3 <= N3 do 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)) <=(N3 * Psign(time + (N-1) * time(check, maxlength(m1), 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(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, signr2, 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)) foreach i3 <= N3 do 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)) <=(N3 * Psign(time + (N-1) * time(check, maxlength(m1), 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(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, signr2, 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, signr2, 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)) foreach i3 <= N3 do 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)) <=(N3 * Psign(time + (N-1) * time(check, maxlength(m1), 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(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, signr2, 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 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_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 with random x and with y and Y independent of x is at most PCollKey2. The other arguments are as in DH_basic. *) def DH_proba_collision(G, Z, g, exp, exp', mult, PCollKey1, PCollKey2) { expand DH_basic(G, Z, g, exp, exp', mult). 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; 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. (* Consequence of the previous equations (since x and y are independent of each other), but CryptoVerif does not infer it. *) 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). } (* 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. 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. *) 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))) <=(0)=> [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) <=(0)=> [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)) <=(0)=> [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)) <=(0)=> [computational] foreach iX <= N do X <-R G; OX() := return(X). } (* 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 secret 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 secret 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" or "nonuniform", and "large") 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. zero is the public key 0. sub_zero is 0, as an element of 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, zero, sub_zero 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, zero, sub_zero) { fun mult(Znw,Znw): Znw. 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 zero:G. const sub_zero:subG. equation zero = subGtoG(sub_zero). const g: G. const g_k:subG. equation pow_k(g) = g_k. fun ZnwtoZ(Znw): Z [data]. param N. equiv(exclude_weak_keys(Z)) foreach i<=N do x <-R Z; O() := return(x) <=(2*N*Pcoll1rand(Z))=> foreach i<=N do x <-R Znw; O() := return(ZnwtoZ(x)). fun exp(G, Z): G. fun exp_div_k(subG,Znw): subG. fun exp_div_k'(subG,Znw): subG. (* X^y = (X^k)^(y/k) *) equation forall X:G, y:Znw; exp(X,ZnwtoZ(y)) = subGtoG(exp_div_k(pow_k(X),y)). (* ((X^(y/k))^k)^(z/k) = X^(y.z/k) Used when expanding exp(exp(g,y),z). *) equation forall X:subG, y:Znw, z:Znw; exp_div_k(pow_k(subGtoG(exp_div_k(X,y))), z) = exp_div_k(X, mult(y,z)). equation forall X:subG, y:Znw, z:Znw; exp_div_k'(pow_k(subGtoG(exp_div_k'(X,y))), z) = exp_div_k'(X, mult(y,z)). equation g_k <> sub_zero. equation forall x:subG, y:Znw; (exp_div_k(x,y) = sub_zero) = (x = sub_zero). equation forall x:subG, y:Znw; (exp_div_k'(x,y) = sub_zero) = (x = sub_zero). equation forall x:subG, y:Znw; (exp_div_k(x,y) <> sub_zero) = (x <> sub_zero). equation forall x:subG, y:Znw; (exp_div_k'(x,y) <> sub_zero) = (x <> sub_zero). equation forall x:subG; (pow_k(subGtoG(x)) = sub_zero) = (x = sub_zero). equation forall x:subG; (pow_k(subGtoG(x)) <> sub_zero) = (x <> sub_zero). equation forall x:subG, x':subG, y:Znw; (exp_div_k(x,y) = exp_div_k(x',y)) = (x = x'). equation forall x:subG, x':subG, y:Znw; (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':Znw, y:Znw; (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':Znw, y:Znw; (exp_div_k'(x,y) = exp_div_k'(g_k, mult(x',y))) = (x = pow_k(subGtoG(exp_div_k'(g_k,x')))). collision y <-R Znw; z <-R Znw; [random_choices_may_be_equal] forall X: subG; return(exp_div_k(X, y) = exp_div_k(X, z)) <=(Pcoll1rand(Znw))=> return((X = sub_zero) || (y = z)). collision y <-R Znw; z <-R Znw; [random_choices_may_be_equal] forall X: subG; return(exp_div_k'(X, y) = exp_div_k'(X, z)) <=(Pcoll1rand(Znw))=> return((X = sub_zero) || (y = z)). collision x <-R Znw; forall X: subG, Y: subG; return(exp_div_k(X, x) = Y) <=(2*Pcoll1rand(Znw))=> return((X = sub_zero) && (Y = sub_zero)) if X independent-of x && Y independent-of x. collision x <-R Znw; forall X: subG, Y: subG; return(exp_div_k'(X, x) = Y) <=(2*Pcoll1rand(Znw))=> return((X = sub_zero) && (Y = sub_zero)) if X independent-of x && Y independent-of x. (* The next collision follows from the previous one and injectivity of pow_k o subGtoG: pow_k(subGtoG(exp_div_k(g_k, y))) = X implies pow_k(subGtoG(exp_div_k(g_k, y))) = pow_k(subGtoG(Y)) from some Y independent of y. By injectivity of pow_k o subGtoG, exp_div_k(g_k, y) = Y By the collision above, this is false except with probability 2*Pcoll1rand(Znw) since (g_k = sub_zero) && (Y = sub_zero) is false and g_k and Y are independent of y. *) collision y <-R Znw; forall X: subG; return(pow_k(subGtoG(exp_div_k(g_k, y))) = X) <=(2*Pcoll1rand(Znw))=> return(false) if X independent-of y. collision y <-R Znw; forall X: subG; return(pow_k(subGtoG(exp_div_k'(g_k, y))) = X) <=(2*Pcoll1rand(Znw))=> return(false) if X independent-of y. (* The next collision follows as the one above, using the injectivity of subGtoG. *) collision y <-R Znw; forall X: G; return(subGtoG(exp_div_k(g_k, y)) = X) <=(2*Pcoll1rand(Znw))=> return(false) if X independent-of y. collision y <-R Znw; forall X: G; return(subGtoG(exp_div_k'(g_k, y)) = X) <=(2*Pcoll1rand(Znw))=> return(false) if X independent-of y. collision x <-R Znw; forall y: Znw, X: subG; return(exp_div_k(g_k,mult(x,y)) = X) <=(2*Pcoll1rand(Znw))=> return(false) if y independent-of x && X independent-of x. collision x <-R Znw; forall y: Znw, X: subG; return(exp_div_k'(g_k,mult(x,y)) = X) <=(2*Pcoll1rand(Znw))=> return(false) if y independent-of x && X independent-of x. (* The next collision is a consequence of the previous one *) collision x <-R Znw; y <-R Znw; forall X: subG; return(exp_div_k(g_k,mult(x,y)) = X) <=(2*Pcoll1rand(Znw))=> return(false) if X independent-of x || X independent-of y. collision x <-R Znw; y <-R Znw; forall X: subG; return(exp_div_k'(g_k,mult(x,y)) = X) <=(2*Pcoll1rand(Znw))=> return(false) if X independent-of x || X independent-of y. collision x <-R Znw; y <-R Znw; [random_choices_may_be_equal] forall X: subG; return(exp_div_k(g_k,mult(x,y)) = X) <=(4*Pcoll1rand(Znw))=> return(false) if X independent-of x || X independent-of y. collision x <-R Znw; y <-R Znw; [random_choices_may_be_equal] forall X: subG; return(exp_div_k'(g_k,mult(x,y)) = X) <=(4*Pcoll1rand(Znw))=> return(false) if X independent-of x || X independent-of y. (* The next collision is a consequence of the previous one, as in DH_good_group *) collision x1 <-R Znw; y1 <-R Znw; x2 <-R Znw; y2 <-R Znw; [random_choices_may_be_equal] return(exp_div_k(g_k,mult(x1,y1)) = exp_div_k(g_k,mult(x2,y2))) <=(4*Pcoll1rand(Znw))=> return(false) if (x1 independent-of x2 || y1 independent-of y2) && (x1 independent-of y2 || y1 independent-of x2). collision x1 <-R Znw; y1 <-R Znw; x2 <-R Znw; y2 <-R Znw; [random_choices_may_be_equal] return(exp_div_k'(g_k,mult(x1,y1)) = exp_div_k'(g_k,mult(x2,y2))) <=(4*Pcoll1rand(Znw))=> return(false) if (x1 independent-of x2 || y1 independent-of y2) && (x1 independent-of y2 || y1 independent-of x2). collision x <-R Znw; y <-R Znw; y' <-R Znw; [random_choices_may_be_equal] return(exp_div_k(g_k,mult(x,y)) = exp_div_k(g_k,mult(x,y'))) <=(Pcoll1rand(Znw))=> return(y = y'). collision x <-R Znw; y <-R Znw; y' <-R Znw; [random_choices_may_be_equal] return(exp_div_k'(g_k,mult(x,y)) = exp_div_k'(g_k,mult(x,y'))) <=(Pcoll1rand(Znw))=> return(y = y'). } (* 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 secret keys are prime to qq'. Therefore, we do not need to exclude weak secret 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, zero, sub_zero) { 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 zero:G. const sub_zero:subG. equation zero = subGtoG(sub_zero). const g: G. const g_k:subG. equation pow_k(g) = g_k. fun exp(G, Z): G. fun exp_div_k(subG,Z): subG. fun exp_div_k'(subG,Z): subG. (* X^y = (X^k)^(y/k) *) equation forall X:G, y:Z; exp(X,y) = subGtoG(exp_div_k(pow_k(X),y)). (* ((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 g_k <> sub_zero. equation forall x:subG, y:Z; (exp_div_k(x,y) = sub_zero) = (x = sub_zero). equation forall x:subG, y:Z; (exp_div_k'(x,y) = sub_zero) = (x = sub_zero). equation forall x:subG, y:Z; (exp_div_k(x,y) <> sub_zero) = (x <> sub_zero). equation forall x:subG, y:Z; (exp_div_k'(x,y) <> sub_zero) = (x <> sub_zero). equation forall x:subG; (pow_k(subGtoG(x)) = sub_zero) = (x = sub_zero). equation forall x:subG; (pow_k(subGtoG(x)) <> sub_zero) = (x <> sub_zero). 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')))). collision y <-R Z; z <-R Z; [random_choices_may_be_equal] forall X: subG; return(exp_div_k(X, y) = exp_div_k(X, z)) <=(Pcoll1rand(Z))=> return((X = sub_zero) || (y = z)). collision y <-R Z; z <-R Z; [random_choices_may_be_equal] forall X: subG; return(exp_div_k'(X, y) = exp_div_k'(X, z)) <=(Pcoll1rand(Z))=> return((X = sub_zero) || (y = z)). collision x <-R Z; forall X: subG, Y: subG; return(exp_div_k(X, x) = Y) <=(2*Pcoll1rand(Z))=> return((X = sub_zero) && (Y = sub_zero)) if X independent-of x && Y independent-of x. collision x <-R Z; forall X: subG, Y: subG; return(exp_div_k'(X, x) = Y) <=(2*Pcoll1rand(Z))=> return((X = sub_zero) && (Y = sub_zero)) if X independent-of x && Y independent-of x. (* The next collision follows from the previous one and injectivity of pow_k o subGtoG: pow_k(subGtoG(exp_div_k(g_k, y))) = X implies pow_k(subGtoG(exp_div_k(g_k, y))) = pow_k(subGtoG(Y)) from some Y independent of y. By injectivity of pow_k o subGtoG, exp_div_k(g_k, y) = Y By the collision above, this is false except with probability 2*Pcoll1rand(Z) since (g_k = sub_zero) && (Y = sub_zero) is false and g_k and Y are independent of y. *) collision y <-R Z; forall X: subG; return(pow_k(subGtoG(exp_div_k(g_k, y))) = X) <=(2*Pcoll1rand(Z))=> return(false) if X independent-of y. collision y <-R Z; forall X: subG; return(pow_k(subGtoG(exp_div_k'(g_k, y))) = X) <=(2*Pcoll1rand(Z))=> return(false) if X independent-of y. (* The next collision follows as the one above, using the injectivity of subGtoG. *) collision y <-R Z; forall X: G; return(subGtoG(exp_div_k(g_k, y)) = X) <=(2*Pcoll1rand(Z))=> return(false) if X independent-of y. collision y <-R Z; forall X: G; return(subGtoG(exp_div_k'(g_k, y)) = X) <=(2*Pcoll1rand(Z))=> return(false) if X independent-of y. collision x <-R Z; forall y: Z, X: subG; return(exp_div_k(g_k,mult(x,y)) = X) <=(2*Pcoll1rand(Z))=> return(false) if y independent-of x && X independent-of x. collision x <-R Z; forall y: Z, X: subG; return(exp_div_k'(g_k,mult(x,y)) = X) <=(2*Pcoll1rand(Z))=> return(false) if y independent-of x && X independent-of x. (* The next collision is a consequence of the previous one *) collision x <-R Z; y <-R Z; forall X: subG; return(exp_div_k(g_k,mult(x,y)) = X) <=(2*Pcoll1rand(Z))=> return(false) if X independent-of x || X independent-of y. collision x <-R Z; y <-R Z; forall X: subG; return(exp_div_k'(g_k,mult(x,y)) = X) <=(2*Pcoll1rand(Z))=> return(false) if X independent-of x || X independent-of y. collision x <-R Z; y <-R Z; [random_choices_may_be_equal] forall X: subG; return(exp_div_k(g_k,mult(x,y)) = X) <=(4*Pcoll1rand(Z))=> return(false) if X independent-of x || X independent-of y. collision x <-R Z; y <-R Z; [random_choices_may_be_equal] forall X: subG; return(exp_div_k'(g_k,mult(x,y)) = X) <=(4*Pcoll1rand(Z))=> return(false) if X independent-of x || X independent-of y. (* The next collision is a consequence of the previous one, as in DH_good_group *) collision x1 <-R Z; y1 <-R Z; x2 <-R Z; y2 <-R Z; [random_choices_may_be_equal] return(exp_div_k(g_k,mult(x1,y1)) = exp_div_k(g_k,mult(x2,y2))) <=(4*Pcoll1rand(Z))=> 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_div_k'(g_k,mult(x1,y1)) = exp_div_k'(g_k,mult(x2,y2))) <=(4*Pcoll1rand(Z))=> return(false) if (x1 independent-of x2 || y1 independent-of y2) && (x1 independent-of y2 || y1 independent-of x2). collision x <-R Z; y <-R Z; y' <-R Z; [random_choices_may_be_equal] return(exp_div_k(g_k,mult(x,y)) = exp_div_k(g_k,mult(x,y'))) <=(Pcoll1rand(Z))=> return(y = y'). collision x <-R Z; y <-R Z; y' <-R Z; [random_choices_may_be_equal] return(exp_div_k'(g_k,mult(x,y)) = exp_div_k'(g_k,mult(x,y'))) <=(Pcoll1rand(Z))=> return(y = y'). } (* 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 assumption 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, zero, sub_zero) { fun mult(Znw,Znw): Znw. 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 zero:G. const sub_zero:subG. equation zero = subGtoG(sub_zero). const g: G. const g_k:subG. equation pow_k(g) = g_k. fun ZnwtoZ(Znw): Z [data]. param N. equiv(exclude_weak_keys(Z)) foreach i<=N do x <-R Z; O() := return(x) <=(2*N*Pcoll1rand(Z))=> foreach i<=N do x <-R Znw; O() := return(ZnwtoZ(x)). fun exp(G, Z): G. fun exp_div_k(subG,Znw): subG. fun exp_div_k'(subG,Znw): subG. (* X^y = (X^k)^(y/k) *) equation forall X:G, y:Znw; exp(X,ZnwtoZ(y)) = subGtoG(exp_div_k(pow_k(X),y)). (* ((X^(y/k))^k)^(z/k) = X^(y.z/k) Used when expanding exp(exp(g,y),z). *) equation forall X:subG, y:Znw, z:Znw; exp_div_k(pow_k(subGtoG(exp_div_k(X,y))), z) = exp_div_k(X, mult(y,z)). equation forall X:subG, y:Znw, z:Znw; exp_div_k'(pow_k(subGtoG(exp_div_k'(X,y))), z) = exp_div_k'(X, mult(y,z)). equation g_k <> sub_zero. equation forall x:subG, y:Znw; (exp_div_k(x,y) = sub_zero) = (x = sub_zero). equation forall x:subG, y:Znw; (exp_div_k'(x,y) = sub_zero) = (x = sub_zero). equation forall x:subG, y:Znw; (exp_div_k(x,y) <> sub_zero) = (x <> sub_zero). equation forall x:subG, y:Znw; (exp_div_k'(x,y) <> sub_zero) = (x <> sub_zero). equation forall x:subG; (pow_k(subGtoG(x)) = sub_zero) = (x = sub_zero). equation forall x:subG; (pow_k(subGtoG(x)) <> sub_zero) = (x <> sub_zero). equation forall x:subG, x':subG, y:Znw; (exp_div_k(x,y) = exp_div_k(x',y)) = (x = x'). equation forall x:subG, x':subG, y:Znw; (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':Znw, y:Znw; (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':Znw, y:Znw; (exp_div_k'(x,y) = exp_div_k'(g_k, mult(x',y))) = (x = pow_k(subGtoG(exp_div_k'(g_k,x')))). collision y <-R Znw; z <-R Znw; [random_choices_may_be_equal] forall X: subG; return(exp_div_k(X, y) = exp_div_k(X, z)) <=(Pcoll1rand(Znw))=> return((X = sub_zero) || (y = z)). collision y <-R Znw; z <-R Znw; [random_choices_may_be_equal] forall X: subG; return(exp_div_k'(X, y) = exp_div_k'(X, z)) <=(Pcoll1rand(Znw))=> return((X = sub_zero) || (y = z)). collision x <-R Znw; forall X: subG, Y: subG; return(exp_div_k(X, x) = Y) <=(2*Pcoll1rand(Znw))=> return((X = sub_zero) && (Y = sub_zero)) if X independent-of x && Y independent-of x. collision x <-R Znw; forall X: subG, Y: subG; return(exp_div_k'(X, x) = Y) <=(2*Pcoll1rand(Znw))=> return((X = sub_zero) && (Y = sub_zero)) if X independent-of x && Y independent-of x. (* The next collision follows from the previous one and injectivity of pow_k o subGtoG: pow_k(subGtoG(exp_div_k(g_k, y))) = X implies pow_k(subGtoG(exp_div_k(g_k, y))) = pow_k(subGtoG(Y)) from some Y independent of y. By injectivity of pow_k o subGtoG, exp_div_k(g_k, y) = Y By the collision above, this is false except with probability 2*Pcoll1rand(Znw) since (g_k = sub_zero) && (Y = sub_zero) is false and g_k and Y are independent of y. *) collision y <-R Znw; forall X: subG; return(pow_k(subGtoG(exp_div_k(g_k, y))) = X) <=(2*Pcoll1rand(Znw))=> return(false) if X independent-of y. collision y <-R Znw; forall X: subG; return(pow_k(subGtoG(exp_div_k'(g_k, y))) = X) <=(2*Pcoll1rand(Znw))=> return(false) if X independent-of y. (* The next collision follows as the one above, using the injectivity of subGtoG. *) collision y <-R Znw; forall X: G; return(subGtoG(exp_div_k(g_k, y)) = X) <=(2*Pcoll1rand(Znw))=> return(false) if X independent-of y. collision y <-R Znw; forall X: G; return(subGtoG(exp_div_k'(g_k, y)) = X) <=(2*Pcoll1rand(Znw))=> return(false) if X independent-of y. collision x <-R Znw; forall y: Znw, X: subG; return(exp_div_k(g_k,mult(x,y)) = X) <=(2*Pcoll1rand(Znw))=> return(false) if y independent-of x && X independent-of x. collision x <-R Znw; forall y: Znw, X: subG; return(exp_div_k'(g_k,mult(x,y)) = X) <=(2*Pcoll1rand(Znw))=> return(false) if y independent-of x && X independent-of x. (* The next collision is a consequence of the previous one *) collision x <-R Znw; y <-R Znw; forall X: subG; return(exp_div_k(g_k,mult(x,y)) = X) <=(2*Pcoll1rand(Znw))=> return(false) if X independent-of x || X independent-of y. collision x <-R Znw; y <-R Znw; forall X: subG; return(exp_div_k'(g_k,mult(x,y)) = X) <=(2*Pcoll1rand(Znw))=> return(false) if X independent-of x || X independent-of y. collision x <-R Znw; y <-R Znw; [random_choices_may_be_equal] forall X: subG; return(exp_div_k(g_k,mult(x,y)) = X) <=(2*Pcoll1rand(Znw))=> return(false) if X independent-of x || X independent-of y. collision x <-R Znw; y <-R Znw; [random_choices_may_be_equal] forall X: subG; return(exp_div_k'(g_k,mult(x,y)) = X) <=(2*Pcoll1rand(Znw))=> return(false) if X independent-of x || X independent-of y. (* The next collision is a consequence of the previous one, as in DH_good_group *) collision x1 <-R Znw; y1 <-R Znw; x2 <-R Znw; y2 <-R Znw; [random_choices_may_be_equal] return(exp_div_k(g_k,mult(x1,y1)) = exp_div_k(g_k,mult(x2,y2))) <=(2*Pcoll1rand(Znw))=> return(false) if (x1 independent-of x2 || y1 independent-of y2) && (x1 independent-of y2 || y1 independent-of x2). collision x1 <-R Znw; y1 <-R Znw; x2 <-R Znw; y2 <-R Znw; [random_choices_may_be_equal] return(exp_div_k'(g_k,mult(x1,y1)) = exp_div_k'(g_k,mult(x2,y2))) <=(2*Pcoll1rand(Znw))=> return(false) if (x1 independent-of x2 || y1 independent-of y2) && (x1 independent-of y2 || y1 independent-of x2). collision x <-R Znw; y <-R Znw; y' <-R Znw; [random_choices_may_be_equal] return(exp_div_k(g_k,mult(x,y)) = exp_div_k(g_k,mult(x,y'))) <=(Pcoll1rand(Znw))=> return(y = y'). collision x <-R Znw; y <-R Znw; y' <-R Znw; [random_choices_may_be_equal] return(exp_div_k'(g_k,mult(x,y)) = exp_div_k'(g_k,mult(x,y'))) <=(Pcoll1rand(Znw))=> return(y = y'). } (* 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. 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 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))) ) <=((naDDH + nbDDH) * na * nb * pCDH(time + (na + nb + #ODDHa + #ODDHb - 3) * 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) := find u<=nb suchthat defined(kb[u],b[u]) && b[j] = b[u] 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 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) := find u<=na suchthat defined(ka[u],a[u]) && a[j] = a[u] 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) ). } (* Variant of CDH with random self reducibility. It may yield lower probabilities but requires the exponents to be chosen uniformly in (Z/qZ)^* or Z/qZ, where q is the order of g, so it is not correct for curve25519 for instance. *) def CDH_RSR(G, Z, g, exp, exp', mult, pCDH) { (* the CDH assumption *) param na, naDDH, nb, nbDDH. 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 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))) ) <=((#ODDHa + #ODDHb) * max(1, 4*#Oa) * max(1, 4*#Ob) * pCDH(time + (na + nb + #ODDHa + #ODDHb) * time(exp)) + (na + na) / |Z|)=> [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) := find u<=nb suchthat defined(kb[u],b[u]) && b[j] = b[u] 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 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) := find u<=na suchthat defined(ka[u],a[u]) && a[j] = a[u] 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) ). } (* 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. 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<=nb) [useful_change] := return (exp(g, mult(b[j], a))) ) | foreach ib <= nb do b <-R Z; ( OB() := return (exp(g,b)) | Ob() [10] := return(b) | foreach ibDH <= nbDH do ODHb(j<=na) := return(exp(g, mult(a[j], b))) ) <=(na * nb * pDDH(time + (na + nb + #ODHa + #ODHb - 3) * time(exp)))=> foreach ia <= na do a <-R Z; ( OA() := return(exp'(g,a)) | Oa() := find uaDH <= naDH suchthat defined(ka'[uaDH]) then event_abort ev_abort else find ubDH <= nbDH, ub <= nb suchthat defined(kb'[ubDH, ub], a'[ubDH, ub]) && a'[ubDH, ub] = a then event_abort ev_abort else let ka:bool = true in return(a) | foreach iaDH <= naDH do ODHa(j<=nb) := let b':Z = b[j] in find u<=nb suchthat defined(kb[u],b[u]) && b' = b[u] then return(exp'(g, mult(b', a))) else if defined(ka) then return(exp'(g, mult(b', a))) else let ka':bool = true in find vaDH <= naDH suchthat defined(b'[vaDH],ca[vaDH]) && b' = b'[vaDH] then return(ca[vaDH]) else find vbDH <= nbDH, vb <= nb suchthat defined(b[vb], a'[vbDH, vb], cb[vbDH, vb]) && b' = b[vb] && a = a'[vbDH, vb] then return(cb[vbDH, vb]) else ca <-R G; return(ca) ) | foreach ib <= nb do b <-R Z; ( OB() := return(exp'(g,b)) | Ob() := find ubDH <= nbDH suchthat defined(kb'[ubDH]) then event_abort ev_abort else find uaDH <= naDH, ua <= na suchthat defined(ka'[uaDH, ua], b'[uaDH, ua]) && b'[uaDH, ua] = b then event_abort ev_abort else let kb:bool = true in return(b) | foreach ibDH <= nbDH do ODHb(j<=na) := let a':Z = a[j] in find u<=na suchthat defined(ka[u],a[u]) && a' = a[u] then return(exp'(g, mult(a', b))) else if defined(kb) then return(exp'(g, mult(a', b))) else let kb':bool = true in find vbDH <= nbDH suchthat defined(a'[vbDH],cb[vbDH]) && a' = a'[vbDH] then return(cb[vbDH]) else find vaDH <= naDH, va <= na suchthat defined(a[va], b'[vaDH, va], ca[vaDH, va]) && a' = a[va] && b = b'[vaDH, va] then return(ca[vaDH, va]) else cb <-R G; return(cb) ). } (* 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. Other arguments as in DH_basic. All arguments must be declared before this macro. *) def GDH(G, Z, g, exp, exp', mult, pGDH) { (* 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, naDDH, naDDH1, naDDH2, naDDH3, naDDH4, naDDH5, naDDH6, naDDH7, naDDH8, naDDH9, nb, nbDDH, nbDDH1, nbDDH2, nbDDH3, nbDDH4, nbDDH5, nbDDH6, nbDDH7, nbDDH8, nbDDH9. 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 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 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 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))) ) <=((naDDH + naDDH4 + naDDH5 + nbDDH + nbDDH6 + nbDDH7) * na * nb * pGDH(time + (na + nb + #ODDHa + #ODDHb - 3) * time(exp), #ODDHa1 + #ODDHa2 + #ODDHa3 + #ODDHa4 + #ODDHa5 + #ODDHa6 + #ODDHa7 + #ODDHa8 + #ODDHb1 + #ODDHb2 + #ODDHb3 + #ODDHb4 + #ODDHb5 + #ODDHb6 + #ODDHb7 + #ODDHb8))=> [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) := find u<=nb suchthat defined(kb[u],b[u]) && b[j'] = b[u] 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(b[j] = b[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 b[j] = b[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 b[j] = b[j'] && exp'(m,b[j']) = exp'(g, mult(b[j'], a)). *) foreach iaDDH5 <= naDDH5 do ODDHa5(m:G, j'<=nb,j<=na) := find u<=nb suchthat defined(kb[u],b[u]) && b[j'] = b[u] 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 (a[j] = a && 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 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) := find u<=nb suchthat defined(kb[u],b[u]) && b[j] = b[u] 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 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) := find u<=na suchthat defined(ka[u],a[u]) && a[j'] = a[u] 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(b[j] = b && exp'(m,b) = exp'(g, mult(a[j'], b))) | foreach ibDDH7 <= nbDDH7 do ODDHb7(m:G, j'<=na,j<=na) := find u<=na suchthat defined(ka[u],a[u]) && a[j'] = a[u] 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(a[j] = a[j'] && exp'(m,a[j']) = exp'(g, mult(a[j'], 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) := find u<=na suchthat defined(ka[u],a[u]) && a[j] = a[u] 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))) ). (* 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. *) } (* Variant of GDH with random self reducibility. It may yield lower probabilities but requires the exponents to be chosen uniformly in (Z/qZ)^* or Z/qZ, where q is the order of g, so it is not correct for curve25519 for instance. *) def GDH_RSR(G, Z, g, exp, exp', mult, pGDH) { (* 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, naDDH, naDDH1, naDDH2, naDDH3, naDDH4, naDDH5, naDDH6, naDDH7, naDDH8, naDDH9, nb, nbDDH, nbDDH1, nbDDH2, nbDDH3, nbDDH4, nbDDH5, nbDDH6, nbDDH7, nbDDH8, nbDDH9. 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 iaDDH5 <= naDDH5 do ODDHa5(m:G, j'<=nb,j<=na) := return(exp(m,a) = exp(g, mult(b[j'], a[j]))) | 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 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 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) = exp(g, mult(a[j'], b[j]))) | 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))) ) <=((#ODDHa + #ODDHb + na * naDDH5 + nb * nbDDH6) * max(1, 4*#Oa) * max(1, 4*#Ob) * pGDH(time + (na + nb + #ODDHa + #ODDHb) * time(exp), #ODDHa1 + #ODDHa2 + #ODDHa3 + #ODDHa5 + #ODDHa6 + #ODDHa7 + #ODDHa8 + #ODDHb1 + #ODDHb2 + #ODDHb3 + #ODDHb4 + #ODDHb5 + #ODDHb6 + #ODDHb8) + (na + nb) / |Z|)=> [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 iaDDH5 <= naDDH5 do ODDHa5(m:G, j'<=nb,j<=na) := find u<=nb suchthat defined(kb[u],b[u]) && b[j'] = b[u] then return(exp'(m,a) = exp'(g, mult(b[j'], a[j]))) else find u<=na suchthat defined(ka[u],a[u]) && a[j] = a[u] then return(exp'(m,a) = exp'(g, mult(b[j'], a[j]))) else return (a[j] = a && exp'(m,a) = exp'(g, mult(b[j'], a))) | (* This case is similar to ODDHb6. *) 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 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) := find u<=nb suchthat defined(kb[u],b[u]) && b[j] = b[u] 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 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) := find u<=na suchthat defined(ka[u],a[u]) && a[j'] = a[u] then return(exp'(m,b) = exp'(g, mult(a[j'], b[j]))) else find u<=nb suchthat defined(kb[u],b[u]) && b[j] = b[u] then return(exp'(m,b) = exp'(g, mult(a[j'], b[j]))) else return(b[j] = b && exp'(m,b) = exp'(g, mult(a[j'], b))) | (* GDH always allows to compute exp(m, b) = exp(g,mult(a[j'],b[j])) as a particular case of ODDHb2. When a[j'] 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) = exp'(g,mult(a[j'],b[j])). Otherwise, we distinguish two cases: - When b = b[j], we also keep the value of the left-hand side. - Otherwise, we apply the CDH assumption considering an adversary that knows b and computes exp(m, b). This adversary cannot compute exp(g,mult(a[j'],b[j])) by CDH, so the equality exp(m, b) = exp(g,mult(a[j'],b[j])) is false in this case. Hence, the equality exp(m, b) = exp(g,mult(a[j'],b[j])) reduces to b[j] = b && exp'(m,b) = exp'(g, mult(a[j], b[j])). *) 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) := find u<=na suchthat defined(ka[u],a[u]) && a[j] = a[u] 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))) ). (* 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. *) } (* 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. 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))) ) <=(na * naDDH * pSQCDH(time + (na + #ODDHa - 2) * time(exp)) + na * (na-1) * naDDH *pCDH(time + (na + #ODDHa - 3) * 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) := find u<=na suchthat defined(ka[u],a[u]) && a[j] = a[u] 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) ). } (* Variant of square CDH with random self reducibility. It may yield lower probabilities but requires the exponents to be chosen uniformly in (Z/qZ)* or Z/qZ, where q is the order of g, so it is not correct for curve25519 for instance. 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_RSR(G, Z, g, exp, exp', mult, pSQCDH) { (* the (square) CDH assumption *) param na, naDDH. 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))) ) <=(#ODDHa * max(1, 27/4*#Oa*#Oa) * pSQCDH(time + (na + #ODDHa) * time(exp)) + na / |Z|)=> [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) := find u<=na suchthat defined(ka[u],a[u]) && a[j] = a[u] 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) ). } (* 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. 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))) ) <=(na * pSQDDH(time + (na + #ODHa - 2) * time(exp)) + na*(na-1)/2 * pDDH(time + (na + #ODHa - 3) * time(exp)))=> foreach ia <= na do a <-R Z; ( OA() := return(exp'(g,a)) | Oa() := find uaDH <= naDH suchthat defined(ka'[uaDH]) then event_abort ev_abort else find uaDH <= naDH, ua <= na suchthat defined(ka'[uaDH, ua], a'[uaDH, ua]) && a'[uaDH, ua] = a then event_abort ev_abort else let ka:bool = true in return(a) | foreach iaDH <= naDH do ODHa(j<=na) := let a':Z = a[j] in find u<=na suchthat defined(ka[u],a[u]) && a' = a[u] then return(exp'(g, mult(a', a))) else if defined(ka) then return(exp'(g, mult(a', a))) else let ka':bool = true in find vaDH <= naDH suchthat defined(a'[vaDH],ca[vaDH]) && a' = a'[vaDH] then return(ca[vaDH]) else find vaDH <= naDH, va <= na suchthat defined(a[va], a'[vaDH, va], ca[vaDH, va]) && a' = a[va] && a = a'[vaDH, va] then return(ca[vaDH, va]) else ca <-R G; return(ca) ). } (* 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. 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) { (* 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, naDDH, naDDH1, naDDH2, naDDH3, naDDH4, naDDH5. 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 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))) ) <=(na * (naDDH + naDDH5) * pSQGDH(time + (na + #ODDHa - 2) * time(exp), #ODDHa1 + #ODDHa3 + #ODDHa5) + na * (na-1) * (naDDH + naDDH5) * pGDH(time + (na + #ODDHa - 3) * time(exp), #ODDHa1 + #ODDHa3 + #ODDHa5))=> [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) := find u<=na suchthat defined(ka[u],a[u]) && a[j'] = a[u] 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 ((a[j] = a || a[j] = a[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 a[j] = a[j'] or a[j] = a[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 (a[j] = a || a[j] = a[j']) && exp'(m, a[j]) = exp'(g,mult(a[j'],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) := find u<=na suchthat defined(ka[u],a[u]) && a[j] = a[u] 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. *) ). (* 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 exponents to be chosen uniformly in (Z/qZ)* or Z/qZ, where q is the order of g, so it is not correct for curve25519 for instance. pSQGDH(t,n): the probability of breaking the square GDH assumption in time t, with at most n calls to the DDH oracle. 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) { (* 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, naDDH, naDDH1, naDDH2, naDDH3, naDDH4, naDDH5. 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) = exp(g, mult(a[j'], a[j]))) | 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))) ) <=((#ODDHa + na * naDDH5) * max(1, 27/4*#Oa*#Oa) * pSQGDH(time + (na + #ODDHa) * time(exp), #ODDHa1 + #ODDHa3 + #ODDHa5) + na / |Z|)=> [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) := find u<=na suchthat defined(ka[u],a[u]) && a[j'] = a[u] then return(exp'(m,a) = exp'(g, mult(a[j'], a[j]))) else find u<=na suchthat defined(ka[u],a[u]) && a[j'] = a[u] then return(exp'(m,a) = exp'(g, mult(a[j'], a[j]))) else return ((a = a[j] || a = a[j']) && exp'(m,a) = exp'(g, mult(a[j'], a[j]))) | (* GDH always allows to compute exp(m, a) = exp(g,mult(a[j'],a[j])) as a particular case of ODDHa2. When a[j] 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) = exp'(g,mult(a[j'],a[j])). Otherwise, we distinguish two cases: - When a = a[j] or a = a[j'], we keep the value that occurs in the left-hand side exp'(m, a) = exp'(g,mult(a[j'],a[j])). - Otherwise, we apply the CDH assumption considering an adversary that knows a and computes exp(m, a). This adversary cannot compute exp(g,mult(a[j'],a[j])) by CDH, so the equality exp(m, a) = exp(g,mult(a[j'],a[j])) is false in this case. Hence, the equality exp(m, a) = exp(g,mult(a[j'],a[j])) reduces to (a = a[j] || a = a[j']) && exp'(m, a) = exp'(g,mult(a[j'],a[j])). *) 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) := find u<=na suchthat defined(ka[u],a[u]) && a[j] = a[u] 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. *) ). (* 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. *) } (********************************* 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 secret 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: secret-key generation function f: the permutation (taking as argument the public key) invf: the inverse permutation of f (taking as argument the secret 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)) foreach iK <= nK do 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] foreach iK <= nK do 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)) foreach iK <= nK do 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] foreach iK <= nK do 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))) <=(max(nK, 4 * #Ox) * 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_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 secret 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: secret-key generation function f: the permutation (taking as argument the public key) invf: the inverse permutation of f (taking as argument the secret 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)) foreach iK <= nK do 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] foreach iK <= nK do 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" hashinput%: type of the %-th input of the hash function hashoutput: type of the output of the hash function, must be "bounded" or "nonuniform" (typically "fixed"), and "large". hash: the hash function. WARNING: hash 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 hashoracle(k) where k is the key. qH is the number of calls to hashoracle. The types key, hashinput%, and hashoutput must be declared before this macro. The function hash, the process hashoracle, 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, hashinput1, hashoutput, hash, hashoracle, qH) { param Nh, N, Neq, Ncoll. fun hash(key, hashinput1):hashoutput. equiv(rom(hash)) foreach ih <= Nh do k <-R key; (foreach i <= N do OH(x1: hashinput1) := return(hash(k, x1)) | foreach ieq <= Neq do Oeq(x1': hashinput1, r': hashoutput) := return(r' = hash(k, x1')) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, z1: hashinput1) := return(hash(k, y1) = hash(k, z1))) <=(#Oeq * Pcoll1rand(hashoutput) + #Ocoll * Pcoll2rand(hashoutput))=> foreach ih <= Nh do (foreach i <= N do OH(x1: hashinput1) := find[unique] u <= N suchthat defined(x1[u], r[u]) && x1 = x1[u] then return(r[u]) else r <-R hashoutput; return(r) | foreach ieq <= Neq do Oeq(x1': hashinput1, r': hashoutput) := find[unique] u <= N suchthat defined(x1[u], r[u]) && x1' = x1[u] then return(r' = r[u]) else return(false) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, z1: hashinput1) := return(y1 = z1)). param qH [noninteractive]. channel ch1, ch2. let hashoracle(k: key) = foreach iH <= qH do in(ch1, (x1: hashinput1)); out(ch2, hash(k, x1)). } def ROM_hash_2(key, hashinput1, hashinput2, hashoutput, hash, hashoracle, qH) { param Nh, N, Neq, Ncoll. fun hash(key, hashinput1, hashinput2):hashoutput. equiv(rom(hash)) foreach ih <= Nh do k <-R key; (foreach i <= N do OH(x1: hashinput1, x2: hashinput2) := return(hash(k, x1, x2)) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, r': hashoutput) := return(r' = hash(k, x1', x2')) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, z1: hashinput1, z2: hashinput2) := return(hash(k, y1, y2) = hash(k, z1, z2))) <=(#Oeq * Pcoll1rand(hashoutput) + #Ocoll * Pcoll2rand(hashoutput))=> foreach ih <= Nh do (foreach i <= N do OH(x1: hashinput1, x2: hashinput2) := find[unique] u <= N suchthat defined(x1[u], x2[u], r[u]) && x1 = x1[u] && x2 = x2[u] then return(r[u]) else r <-R hashoutput; return(r) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, r': hashoutput) := find[unique] u <= N suchthat defined(x1[u], x2[u], r[u]) && x1' = x1[u] && x2' = x2[u] then return(r' = r[u]) else return(false) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, z1: hashinput1, z2: hashinput2) := return(y1 = z1 && y2 = z2)). param qH [noninteractive]. channel ch1, ch2. let hashoracle(k: key) = foreach iH <= qH do in(ch1, (x1: hashinput1, x2: hashinput2)); out(ch2, hash(k, x1, x2)). } def ROM_hash_3(key, hashinput1, hashinput2, hashinput3, hashoutput, hash, hashoracle, qH) { param Nh, N, Neq, Ncoll. fun hash(key, hashinput1, hashinput2, hashinput3):hashoutput. equiv(rom(hash)) foreach ih <= Nh do k <-R key; (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3) := return(hash(k, x1, x2, x3)) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, r': hashoutput) := return(r' = hash(k, x1', x2', x3')) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, z1: hashinput1, z2: hashinput2, z3: hashinput3) := return(hash(k, y1, y2, y3) = hash(k, z1, z2, z3))) <=(#Oeq * Pcoll1rand(hashoutput) + #Ocoll * Pcoll2rand(hashoutput))=> foreach ih <= Nh do (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], r[u]) && x1 = x1[u] && x2 = x2[u] && x3 = x3[u] then return(r[u]) else r <-R hashoutput; return(r) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, r': hashoutput) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], r[u]) && x1' = x1[u] && x2' = x2[u] && x3' = x3[u] then return(r' = r[u]) else return(false) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, z1: hashinput1, z2: hashinput2, z3: hashinput3) := return(y1 = z1 && y2 = z2 && y3 = z3)). param qH [noninteractive]. channel ch1, ch2. let hashoracle(k: key) = foreach iH <= qH do in(ch1, (x1: hashinput1, x2: hashinput2, x3: hashinput3)); out(ch2, hash(k, x1, x2, x3)). } def ROM_hash_4(key, hashinput1, hashinput2, hashinput3, hashinput4, hashoutput, hash, hashoracle, qH) { param Nh, N, Neq, Ncoll. fun hash(key, hashinput1, hashinput2, hashinput3, hashinput4):hashoutput. equiv(rom(hash)) foreach ih <= Nh do k <-R key; (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4) := return(hash(k, x1, x2, x3, x4)) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, r': hashoutput) := return(r' = hash(k, x1', x2', x3', x4')) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4) := return(hash(k, y1, y2, y3, y4) = hash(k, z1, z2, z3, z4))) <=(#Oeq * Pcoll1rand(hashoutput) + #Ocoll * Pcoll2rand(hashoutput))=> foreach ih <= Nh do (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], r[u]) && x1 = x1[u] && x2 = x2[u] && x3 = x3[u] && x4 = x4[u] then return(r[u]) else r <-R hashoutput; return(r) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, r': hashoutput) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], r[u]) && x1' = x1[u] && x2' = x2[u] && x3' = x3[u] && x4' = x4[u] then return(r' = r[u]) else return(false) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4) := return(y1 = z1 && y2 = z2 && y3 = z3 && y4 = z4)). param qH [noninteractive]. channel ch1, ch2. let hashoracle(k: key) = foreach iH <= qH do in(ch1, (x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4)); out(ch2, hash(k, x1, x2, x3, x4)). } def ROM_hash_5(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashoutput, hash, hashoracle, qH) { param Nh, N, Neq, Ncoll. fun hash(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5):hashoutput. equiv(rom(hash)) foreach ih <= Nh do k <-R key; (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5) := return(hash(k, x1, x2, x3, x4, x5)) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, x5': hashinput5, r': hashoutput) := return(r' = hash(k, x1', x2', x3', x4', x5')) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, y5: hashinput5, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4, z5: hashinput5) := return(hash(k, y1, y2, y3, y4, y5) = hash(k, z1, z2, z3, z4, z5))) <=(#Oeq * Pcoll1rand(hashoutput) + #Ocoll * Pcoll2rand(hashoutput))=> foreach ih <= Nh do (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], x5[u], r[u]) && x1 = x1[u] && x2 = x2[u] && x3 = x3[u] && x4 = x4[u] && x5 = x5[u] then return(r[u]) else r <-R hashoutput; return(r) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, x5': hashinput5, r': hashoutput) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], x5[u], r[u]) && x1' = x1[u] && x2' = x2[u] && x3' = x3[u] && x4' = x4[u] && x5' = x5[u] then return(r' = r[u]) else return(false) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, y5: hashinput5, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4, z5: hashinput5) := return(y1 = z1 && y2 = z2 && y3 = z3 && y4 = z4 && y5 = z5)). param qH [noninteractive]. channel ch1, ch2. let hashoracle(k: key) = foreach iH <= qH do in(ch1, (x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5)); out(ch2, hash(k, x1, x2, x3, x4, x5)). } def ROM_hash_6(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashoutput, hash, hashoracle, qH) { param Nh, N, Neq, Ncoll. fun hash(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6):hashoutput. equiv(rom(hash)) foreach ih <= Nh do k <-R key; (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6) := return(hash(k, x1, x2, x3, x4, x5, x6)) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, x5': hashinput5, x6': hashinput6, r': hashoutput) := return(r' = hash(k, x1', x2', x3', x4', x5', x6')) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, y5: hashinput5, y6: hashinput6, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4, z5: hashinput5, z6: hashinput6) := return(hash(k, y1, y2, y3, y4, y5, y6) = hash(k, z1, z2, z3, z4, z5, z6))) <=(#Oeq * Pcoll1rand(hashoutput) + #Ocoll * Pcoll2rand(hashoutput))=> foreach ih <= Nh do (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], x5[u], x6[u], r[u]) && x1 = x1[u] && x2 = x2[u] && x3 = x3[u] && x4 = x4[u] && x5 = x5[u] && x6 = x6[u] then return(r[u]) else r <-R hashoutput; return(r) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, x5': hashinput5, x6': hashinput6, r': hashoutput) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], x5[u], x6[u], r[u]) && x1' = x1[u] && x2' = x2[u] && x3' = x3[u] && x4' = x4[u] && x5' = x5[u] && x6' = x6[u] then return(r' = r[u]) else return(false) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, y5: hashinput5, y6: hashinput6, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4, z5: hashinput5, z6: hashinput6) := return(y1 = z1 && y2 = z2 && y3 = z3 && y4 = z4 && y5 = z5 && y6 = z6)). param qH [noninteractive]. channel ch1, ch2. let hashoracle(k: key) = foreach iH <= qH do in(ch1, (x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6)); out(ch2, hash(k, x1, x2, x3, x4, x5, x6)). } def ROM_hash_7(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashinput7, hashoutput, hash, hashoracle, qH) { param Nh, N, Neq, Ncoll. fun hash(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashinput7):hashoutput. equiv(rom(hash)) foreach ih <= Nh do k <-R key; (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6, x7: hashinput7) := return(hash(k, x1, x2, x3, x4, x5, x6, x7)) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, x5': hashinput5, x6': hashinput6, x7': hashinput7, r': hashoutput) := return(r' = hash(k, x1', x2', x3', x4', x5', x6', x7')) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, y5: hashinput5, y6: hashinput6, y7: hashinput7, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4, z5: hashinput5, z6: hashinput6, z7: hashinput7) := return(hash(k, y1, y2, y3, y4, y5, y6, y7) = hash(k, z1, z2, z3, z4, z5, z6, z7))) <=(#Oeq * Pcoll1rand(hashoutput) + #Ocoll * Pcoll2rand(hashoutput))=> foreach ih <= Nh do (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6, x7: hashinput7) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], x5[u], x6[u], x7[u], r[u]) && x1 = x1[u] && x2 = x2[u] && x3 = x3[u] && x4 = x4[u] && x5 = x5[u] && x6 = x6[u] && x7 = x7[u] then return(r[u]) else r <-R hashoutput; return(r) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, x5': hashinput5, x6': hashinput6, x7': hashinput7, r': hashoutput) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], x5[u], x6[u], x7[u], r[u]) && x1' = x1[u] && x2' = x2[u] && x3' = x3[u] && x4' = x4[u] && x5' = x5[u] && x6' = x6[u] && x7' = x7[u] then return(r' = r[u]) else return(false) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, y5: hashinput5, y6: hashinput6, y7: hashinput7, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4, z5: hashinput5, z6: hashinput6, z7: hashinput7) := return(y1 = z1 && y2 = z2 && y3 = z3 && y4 = z4 && y5 = z5 && y6 = z6 && y7 = z7)). param qH [noninteractive]. channel ch1, ch2. let hashoracle(k: key) = foreach iH <= qH do in(ch1, (x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6, x7: hashinput7)); out(ch2, hash(k, x1, x2, x3, x4, x5, x6, x7)). } def ROM_hash_8(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashinput7, hashinput8, hashoutput, hash, hashoracle, qH) { param Nh, N, Neq, Ncoll. fun hash(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashinput7, hashinput8):hashoutput. equiv(rom(hash)) foreach ih <= Nh do k <-R key; (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6, x7: hashinput7, x8: hashinput8) := return(hash(k, x1, x2, x3, x4, x5, x6, x7, x8)) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, x5': hashinput5, x6': hashinput6, x7': hashinput7, x8': hashinput8, r': hashoutput) := return(r' = hash(k, x1', x2', x3', x4', x5', x6', x7', x8')) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, y5: hashinput5, y6: hashinput6, y7: hashinput7, y8: hashinput8, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4, z5: hashinput5, z6: hashinput6, z7: hashinput7, z8: hashinput8) := return(hash(k, y1, y2, y3, y4, y5, y6, y7, y8) = hash(k, z1, z2, z3, z4, z5, z6, z7, z8))) <=(#Oeq * Pcoll1rand(hashoutput) + #Ocoll * Pcoll2rand(hashoutput))=> foreach ih <= Nh do (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6, x7: hashinput7, x8: hashinput8) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], x5[u], x6[u], x7[u], x8[u], r[u]) && x1 = x1[u] && x2 = x2[u] && x3 = x3[u] && x4 = x4[u] && x5 = x5[u] && x6 = x6[u] && x7 = x7[u] && x8 = x8[u] then return(r[u]) else r <-R hashoutput; return(r) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, x5': hashinput5, x6': hashinput6, x7': hashinput7, x8': hashinput8, r': hashoutput) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], x5[u], x6[u], x7[u], x8[u], r[u]) && x1' = x1[u] && x2' = x2[u] && x3' = x3[u] && x4' = x4[u] && x5' = x5[u] && x6' = x6[u] && x7' = x7[u] && x8' = x8[u] then return(r' = r[u]) else return(false) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, y5: hashinput5, y6: hashinput6, y7: hashinput7, y8: hashinput8, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4, z5: hashinput5, z6: hashinput6, z7: hashinput7, z8: hashinput8) := return(y1 = z1 && y2 = z2 && y3 = z3 && y4 = z4 && y5 = z5 && y6 = z6 && y7 = z7 && y8 = z8)). param qH [noninteractive]. channel ch1, ch2. let hashoracle(k: key) = foreach iH <= qH do in(ch1, (x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6, x7: hashinput7, x8: hashinput8)); out(ch2, hash(k, x1, x2, x3, x4, x5, x6, x7, x8)). } def ROM_hash_9(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashinput7, hashinput8, hashinput9, hashoutput, hash, hashoracle, qH) { param Nh, N, Neq, Ncoll. fun hash(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashinput7, hashinput8, hashinput9):hashoutput. equiv(rom(hash)) foreach ih <= Nh do k <-R key; (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6, x7: hashinput7, x8: hashinput8, x9: hashinput9) := return(hash(k, x1, x2, x3, x4, x5, x6, x7, x8, x9)) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, x5': hashinput5, x6': hashinput6, x7': hashinput7, x8': hashinput8, x9': hashinput9, r': hashoutput) := return(r' = hash(k, x1', x2', x3', x4', x5', x6', x7', x8', x9')) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, y5: hashinput5, y6: hashinput6, y7: hashinput7, y8: hashinput8, y9: hashinput9, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4, z5: hashinput5, z6: hashinput6, z7: hashinput7, z8: hashinput8, z9: hashinput9) := return(hash(k, y1, y2, y3, y4, y5, y6, y7, y8, y9) = hash(k, z1, z2, z3, z4, z5, z6, z7, z8, z9))) <=(#Oeq * Pcoll1rand(hashoutput) + #Ocoll * Pcoll2rand(hashoutput))=> foreach ih <= Nh do (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6, x7: hashinput7, x8: hashinput8, x9: hashinput9) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], x5[u], x6[u], x7[u], x8[u], x9[u], r[u]) && x1 = x1[u] && x2 = x2[u] && x3 = x3[u] && x4 = x4[u] && x5 = x5[u] && x6 = x6[u] && x7 = x7[u] && x8 = x8[u] && x9 = x9[u] then return(r[u]) else r <-R hashoutput; return(r) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, x5': hashinput5, x6': hashinput6, x7': hashinput7, x8': hashinput8, x9': hashinput9, r': hashoutput) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], x5[u], x6[u], x7[u], x8[u], x9[u], r[u]) && x1' = x1[u] && x2' = x2[u] && x3' = x3[u] && x4' = x4[u] && x5' = x5[u] && x6' = x6[u] && x7' = x7[u] && x8' = x8[u] && x9' = x9[u] then return(r' = r[u]) else return(false) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, y5: hashinput5, y6: hashinput6, y7: hashinput7, y8: hashinput8, y9: hashinput9, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4, z5: hashinput5, z6: hashinput6, z7: hashinput7, z8: hashinput8, z9: hashinput9) := return(y1 = z1 && y2 = z2 && y3 = z3 && y4 = z4 && y5 = z5 && y6 = z6 && y7 = z7 && y8 = z8 && y9 = z9)). param qH [noninteractive]. channel ch1, ch2. let hashoracle(k: key) = foreach iH <= qH do in(ch1, (x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6, x7: hashinput7, x8: hashinput8, x9: hashinput9)); out(ch2, hash(k, x1, x2, x3, x4, x5, x6, x7, x8, x9)). } def ROM_hash_10(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashinput7, hashinput8, hashinput9, hashinput10, hashoutput, hash, hashoracle, qH) { param Nh, N, Neq, Ncoll. fun hash(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashinput7, hashinput8, hashinput9, hashinput10):hashoutput. equiv(rom(hash)) foreach ih <= Nh do k <-R key; (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6, x7: hashinput7, x8: hashinput8, x9: hashinput9, x10: hashinput10) := return(hash(k, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, x5': hashinput5, x6': hashinput6, x7': hashinput7, x8': hashinput8, x9': hashinput9, x10': hashinput10, r': hashoutput) := return(r' = hash(k, x1', x2', x3', x4', x5', x6', x7', x8', x9', x10')) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, y5: hashinput5, y6: hashinput6, y7: hashinput7, y8: hashinput8, y9: hashinput9, y10: hashinput10, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4, z5: hashinput5, z6: hashinput6, z7: hashinput7, z8: hashinput8, z9: hashinput9, z10: hashinput10) := return(hash(k, y1, y2, y3, y4, y5, y6, y7, y8, y9, y10) = hash(k, z1, z2, z3, z4, z5, z6, z7, z8, z9, z10))) <=(#Oeq * Pcoll1rand(hashoutput) + #Ocoll * Pcoll2rand(hashoutput))=> foreach ih <= Nh do (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6, x7: hashinput7, x8: hashinput8, x9: hashinput9, x10: hashinput10) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], x5[u], x6[u], x7[u], x8[u], x9[u], x10[u], r[u]) && x1 = x1[u] && x2 = x2[u] && x3 = x3[u] && x4 = x4[u] && x5 = x5[u] && x6 = x6[u] && x7 = x7[u] && x8 = x8[u] && x9 = x9[u] && x10 = x10[u] then return(r[u]) else r <-R hashoutput; return(r) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, x5': hashinput5, x6': hashinput6, x7': hashinput7, x8': hashinput8, x9': hashinput9, x10': hashinput10, r': hashoutput) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], x5[u], x6[u], x7[u], x8[u], x9[u], x10[u], r[u]) && x1' = x1[u] && x2' = x2[u] && x3' = x3[u] && x4' = x4[u] && x5' = x5[u] && x6' = x6[u] && x7' = x7[u] && x8' = x8[u] && x9' = x9[u] && x10' = x10[u] then return(r' = r[u]) else return(false) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, y5: hashinput5, y6: hashinput6, y7: hashinput7, y8: hashinput8, y9: hashinput9, y10: hashinput10, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4, z5: hashinput5, z6: hashinput6, z7: hashinput7, z8: hashinput8, z9: hashinput9, z10: hashinput10) := return(y1 = z1 && y2 = z2 && y3 = z3 && y4 = z4 && y5 = z5 && y6 = z6 && y7 = z7 && y8 = z8 && y9 = z9 && y10 = z10)). param qH [noninteractive]. channel ch1, ch2. let hashoracle(k: key) = foreach iH <= qH do in(ch1, (x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6, x7: hashinput7, x8: hashinput8, x9: hashinput9, x10: hashinput10)); out(ch2, hash(k, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)). } def ROM_hash(key, hashinput, hashoutput, hash, hashoracle, qH) { expand ROM_hash_1(key, hashinput, hashoutput, hash, hashoracle, qH). } (* Collision resistant hash function key: type of the key of the hash function, must be "bounded" or "nonuniform", typically "fixed" hashinput%: type of the %-th input of the hash function hashoutput: type of the output of the hash function hash: 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 hashoracle(k), where k is the key. The types key, hashinput%, hashoutput, and the probability Phash must be declared before this macro. The function hash and the process hashoracle 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, hashinput1, hashoutput, hash, hashoracle, Phash) { fun hash(key, hashinput1):hashoutput. collision k <-R key; forall x1:hashinput1, y1:hashinput1; return(hash(k, x1) = hash(k, y1)) <=(Phash(time))=> return((x1 = y1)). channel ch1, ch2. let hashoracle(k: key) = in(ch1, ()); out(ch2, k). } def CollisionResistant_hash_2(key, hashinput1, hashinput2, hashoutput, hash, hashoracle, Phash) { fun hash(key, hashinput1, hashinput2):hashoutput. collision k <-R key; forall x1:hashinput1, x2:hashinput2, y1:hashinput1, y2:hashinput2; return(hash(k, x1, x2) = hash(k, y1, y2)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2)). channel ch1, ch2. let hashoracle(k: key) = in(ch1, ()); out(ch2, k). } def CollisionResistant_hash_3(key, hashinput1, hashinput2, hashinput3, hashoutput, hash, hashoracle, Phash) { fun hash(key, hashinput1, hashinput2, hashinput3):hashoutput. collision k <-R key; forall x1:hashinput1, x2:hashinput2, x3:hashinput3, y1:hashinput1, y2:hashinput2, y3:hashinput3; return(hash(k, x1, x2, x3) = hash(k, y1, y2, y3)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3)). channel ch1, ch2. let hashoracle(k: key) = in(ch1, ()); out(ch2, k). } def CollisionResistant_hash_4(key, hashinput1, hashinput2, hashinput3, hashinput4, hashoutput, hash, hashoracle, Phash) { fun hash(key, hashinput1, hashinput2, hashinput3, hashinput4):hashoutput. collision k <-R key; forall x1:hashinput1, x2:hashinput2, x3:hashinput3, x4:hashinput4, y1:hashinput1, y2:hashinput2, y3:hashinput3, y4:hashinput4; return(hash(k, x1, x2, x3, x4) = hash(k, y1, y2, y3, y4)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4)). channel ch1, ch2. let hashoracle(k: key) = in(ch1, ()); out(ch2, k). } def CollisionResistant_hash_5(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashoutput, hash, hashoracle, Phash) { fun hash(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5):hashoutput. collision k <-R key; forall x1:hashinput1, x2:hashinput2, x3:hashinput3, x4:hashinput4, x5:hashinput5, y1:hashinput1, y2:hashinput2, y3:hashinput3, y4:hashinput4, y5:hashinput5; return(hash(k, x1, x2, x3, x4, x5) = hash(k, y1, y2, y3, y4, y5)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5)). channel ch1, ch2. let hashoracle(k: key) = in(ch1, ()); out(ch2, k). } def CollisionResistant_hash_6(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashoutput, hash, hashoracle, Phash) { fun hash(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6):hashoutput. collision k <-R key; forall x1:hashinput1, x2:hashinput2, x3:hashinput3, x4:hashinput4, x5:hashinput5, x6:hashinput6, y1:hashinput1, y2:hashinput2, y3:hashinput3, y4:hashinput4, y5:hashinput5, y6:hashinput6; return(hash(k, x1, x2, x3, x4, x5, x6) = hash(k, y1, y2, y3, y4, y5, y6)) <=(Phash(time))=> return((x1 = y1) && (x2 = y2) && (x3 = y3) && (x4 = y4) && (x5 = y5) && (x6 = y6)). channel ch1, ch2. let hashoracle(k: key) = in(ch1, ()); out(ch2, k). } def CollisionResistant_hash_7(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashinput7, hashoutput, hash, hashoracle, Phash) { fun hash(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashinput7):hashoutput. collision k <-R key; forall x1:hashinput1, x2:hashinput2, x3:hashinput3, x4:hashinput4, x5:hashinput5, x6:hashinput6, x7:hashinput7, y1:hashinput1, y2:hashinput2, y3:hashinput3, y4:hashinput4, y5:hashinput5, y6:hashinput6, y7:hashinput7; return(hash(k, x1, x2, x3, x4, x5, x6, x7) = hash(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)). channel ch1, ch2. let hashoracle(k: key) = in(ch1, ()); out(ch2, k). } def CollisionResistant_hash_8(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashinput7, hashinput8, hashoutput, hash, hashoracle, Phash) { fun hash(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashinput7, hashinput8):hashoutput. collision k <-R key; forall x1:hashinput1, x2:hashinput2, x3:hashinput3, x4:hashinput4, x5:hashinput5, x6:hashinput6, x7:hashinput7, x8:hashinput8, y1:hashinput1, y2:hashinput2, y3:hashinput3, y4:hashinput4, y5:hashinput5, y6:hashinput6, y7:hashinput7, y8:hashinput8; return(hash(k, x1, x2, x3, x4, x5, x6, x7, x8) = hash(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)). channel ch1, ch2. let hashoracle(k: key) = in(ch1, ()); out(ch2, k). } def CollisionResistant_hash_9(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashinput7, hashinput8, hashinput9, hashoutput, hash, hashoracle, Phash) { fun hash(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashinput7, hashinput8, hashinput9):hashoutput. collision k <-R key; forall x1:hashinput1, x2:hashinput2, x3:hashinput3, x4:hashinput4, x5:hashinput5, x6:hashinput6, x7:hashinput7, x8:hashinput8, x9:hashinput9, y1:hashinput1, y2:hashinput2, y3:hashinput3, y4:hashinput4, y5:hashinput5, y6:hashinput6, y7:hashinput7, y8:hashinput8, y9:hashinput9; return(hash(k, x1, x2, x3, x4, x5, x6, x7, x8, x9) = hash(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)). channel ch1, ch2. let hashoracle(k: key) = in(ch1, ()); out(ch2, k). } def CollisionResistant_hash_10(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashinput7, hashinput8, hashinput9, hashinput10, hashoutput, hash, hashoracle, Phash) { fun hash(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashinput7, hashinput8, hashinput9, hashinput10):hashoutput. collision k <-R key; forall x1:hashinput1, x2:hashinput2, x3:hashinput3, x4:hashinput4, x5:hashinput5, x6:hashinput6, x7:hashinput7, x8:hashinput8, x9:hashinput9, x10:hashinput10, y1:hashinput1, y2:hashinput2, y3:hashinput3, y4:hashinput4, y5:hashinput5, y6:hashinput6, y7:hashinput7, y8:hashinput8, y9:hashinput9, y10:hashinput10; return(hash(k, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10) = hash(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)). channel ch1, ch2. let hashoracle(k: key) = in(ch1, ()); out(ch2, k). } def CollisionResistant_hash(key, hashinput, hashoutput, hash, hashoracle, Phash) { expand CollisionResistant_hash_1(key, hashinput, hashoutput, hash, hashoracle, 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) { param N, N2. fun f(key, input1): output. equiv(prf(f)) foreach i2 <= N2 do k <-R key; foreach i <= N do Of(x1:input1) := return(f(k, x1)) <=(N2 * Pprf(time + (N2-1)*N*time(f, maxlength(x1)), N, maxlength(x1)))=> foreach i2 <= N2 do foreach i <= N do Of(x1:input1) := find[unique] j<=N suchthat defined(x1[j],r[j]) && (x1 = x1[j]) then return(r[j]) else r <-R output; return(r). } def PRF_2(key, input1, input2, output, f, Pprf) { param N, N2. fun f(key, input1, input2): output. equiv(prf(f)) foreach i2 <= N2 do k <-R key; foreach i <= N do Of(x1:input1, x2:input2) := return(f(k, x1, x2)) <=(N2 * Pprf(time + (N2-1)*N*time(f, maxlength(x1), maxlength(x2)), N, maxlength(x1), maxlength(x2)))=> foreach i2 <= N2 do foreach i <= N do Of(x1:input1, x2:input2) := find[unique] j<=N suchthat defined(x1[j], x2[j],r[j]) && (x1 = x1[j]) && (x2 = x2[j]) then return(r[j]) else r <-R output; return(r). } def PRF_3(key, input1, input2, input3, output, f, Pprf) { param N, N2. fun f(key, input1, input2, input3): output. equiv(prf(f)) foreach i2 <= N2 do k <-R key; foreach i <= N do Of(x1:input1, x2:input2, x3:input3) := return(f(k, x1, x2, x3)) <=(N2 * Pprf(time + (N2-1)*N*time(f, maxlength(x1), maxlength(x2), maxlength(x3)), N, maxlength(x1), maxlength(x2), maxlength(x3)))=> foreach i2 <= N2 do foreach i <= N do Of(x1:input1, x2:input2, x3:input3) := find[unique] j<=N suchthat defined(x1[j], x2[j], x3[j],r[j]) && (x1 = x1[j]) && (x2 = x2[j]) && (x3 = x3[j]) then return(r[j]) else r <-R output; return(r). } def PRF_4(key, input1, input2, input3, input4, output, f, Pprf) { param N, N2. fun f(key, input1, input2, input3, input4): output. equiv(prf(f)) foreach i2 <= N2 do k <-R key; foreach i <= N do Of(x1:input1, x2:input2, x3:input3, x4:input4) := return(f(k, x1, x2, x3, x4)) <=(N2 * Pprf(time + (N2-1)*N*time(f, maxlength(x1), maxlength(x2), maxlength(x3), maxlength(x4)), N, maxlength(x1), maxlength(x2), maxlength(x3), maxlength(x4)))=> foreach i2 <= N2 do foreach i <= N do Of(x1:input1, x2:input2, x3:input3, x4:input4) := find[unique] j<=N suchthat defined(x1[j], x2[j], x3[j], x4[j],r[j]) && (x1 = x1[j]) && (x2 = x2[j]) && (x3 = x3[j]) && (x4 = x4[j]) then return(r[j]) else r <-R output; return(r). } def PRF_5(key, input1, input2, input3, input4, input5, output, f, Pprf) { param N, N2. fun f(key, input1, input2, input3, input4, input5): output. equiv(prf(f)) foreach i2 <= N2 do k <-R key; foreach i <= N do Of(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5) := return(f(k, x1, x2, x3, x4, x5)) <=(N2 * Pprf(time + (N2-1)*N*time(f, maxlength(x1), maxlength(x2), maxlength(x3), maxlength(x4), maxlength(x5)), N, maxlength(x1), maxlength(x2), maxlength(x3), maxlength(x4), maxlength(x5)))=> foreach i2 <= N2 do foreach i <= N do Of(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5) := find[unique] j<=N suchthat defined(x1[j], x2[j], x3[j], x4[j], x5[j],r[j]) && (x1 = x1[j]) && (x2 = x2[j]) && (x3 = x3[j]) && (x4 = x4[j]) && (x5 = x5[j]) then return(r[j]) else r <-R output; return(r). } def PRF_6(key, input1, input2, input3, input4, input5, input6, output, f, Pprf) { param N, N2. fun f(key, input1, input2, input3, input4, input5, input6): output. equiv(prf(f)) foreach i2 <= N2 do k <-R key; foreach i <= N do Of(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6) := return(f(k, x1, x2, x3, x4, x5, x6)) <=(N2 * Pprf(time + (N2-1)*N*time(f, maxlength(x1), maxlength(x2), maxlength(x3), maxlength(x4), maxlength(x5), maxlength(x6)), N, maxlength(x1), maxlength(x2), maxlength(x3), maxlength(x4), maxlength(x5), maxlength(x6)))=> foreach i2 <= N2 do foreach i <= N do Of(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6) := find[unique] j<=N suchthat defined(x1[j], x2[j], x3[j], x4[j], x5[j], x6[j],r[j]) && (x1 = x1[j]) && (x2 = x2[j]) && (x3 = x3[j]) && (x4 = x4[j]) && (x5 = x5[j]) && (x6 = x6[j]) then return(r[j]) else r <-R output; return(r). } def PRF_7(key, input1, input2, input3, input4, input5, input6, input7, output, f, Pprf) { param N, N2. fun f(key, input1, input2, input3, input4, input5, input6, input7): output. equiv(prf(f)) foreach i2 <= N2 do k <-R key; foreach i <= N do Of(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7) := return(f(k, x1, x2, x3, x4, x5, x6, x7)) <=(N2 * Pprf(time + (N2-1)*N*time(f, maxlength(x1), maxlength(x2), maxlength(x3), maxlength(x4), maxlength(x5), maxlength(x6), maxlength(x7)), N, maxlength(x1), maxlength(x2), maxlength(x3), maxlength(x4), maxlength(x5), maxlength(x6), maxlength(x7)))=> foreach i2 <= N2 do foreach i <= N do Of(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7) := find[unique] j<=N suchthat defined(x1[j], x2[j], x3[j], x4[j], x5[j], x6[j], x7[j],r[j]) && (x1 = x1[j]) && (x2 = x2[j]) && (x3 = x3[j]) && (x4 = x4[j]) && (x5 = x5[j]) && (x6 = x6[j]) && (x7 = x7[j]) then return(r[j]) else r <-R output; return(r). } def PRF_8(key, input1, input2, input3, input4, input5, input6, input7, input8, output, f, Pprf) { param N, N2. fun f(key, input1, input2, input3, input4, input5, input6, input7, input8): output. equiv(prf(f)) foreach i2 <= N2 do k <-R key; foreach i <= N do Of(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)) <=(N2 * Pprf(time + (N2-1)*N*time(f, maxlength(x1), maxlength(x2), maxlength(x3), maxlength(x4), maxlength(x5), maxlength(x6), maxlength(x7), maxlength(x8)), N, maxlength(x1), maxlength(x2), maxlength(x3), maxlength(x4), maxlength(x5), maxlength(x6), maxlength(x7), maxlength(x8)))=> foreach i2 <= N2 do foreach i <= N do Of(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7, x8:input8) := find[unique] j<=N suchthat defined(x1[j], x2[j], x3[j], x4[j], x5[j], x6[j], x7[j], x8[j],r[j]) && (x1 = x1[j]) && (x2 = x2[j]) && (x3 = x3[j]) && (x4 = x4[j]) && (x5 = x5[j]) && (x6 = x6[j]) && (x7 = x7[j]) && (x8 = x8[j]) then return(r[j]) else r <-R output; return(r). } def PRF_9(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, output, f, Pprf) { param N, N2. fun f(key, input1, input2, input3, input4, input5, input6, input7, input8, input9): output. equiv(prf(f)) foreach i2 <= N2 do k <-R key; foreach i <= N do Of(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)) <=(N2 * Pprf(time + (N2-1)*N*time(f, maxlength(x1), maxlength(x2), maxlength(x3), maxlength(x4), maxlength(x5), maxlength(x6), maxlength(x7), maxlength(x8), maxlength(x9)), N, maxlength(x1), maxlength(x2), maxlength(x3), maxlength(x4), maxlength(x5), maxlength(x6), maxlength(x7), maxlength(x8), maxlength(x9)))=> foreach i2 <= N2 do foreach i <= N do Of(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7, x8:input8, x9:input9) := find[unique] j<=N suchthat defined(x1[j], x2[j], x3[j], x4[j], x5[j], x6[j], x7[j], x8[j], x9[j],r[j]) && (x1 = x1[j]) && (x2 = x2[j]) && (x3 = x3[j]) && (x4 = x4[j]) && (x5 = x5[j]) && (x6 = x6[j]) && (x7 = x7[j]) && (x8 = x8[j]) && (x9 = x9[j]) then return(r[j]) else r <-R output; return(r). } def PRF_10(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10, output, f, Pprf) { param N, N2. fun f(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10): output. equiv(prf(f)) foreach i2 <= N2 do k <-R key; foreach i <= N do Of(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)) <=(N2 * Pprf(time + (N2-1)*N*time(f, maxlength(x1), maxlength(x2), maxlength(x3), maxlength(x4), maxlength(x5), maxlength(x6), maxlength(x7), maxlength(x8), maxlength(x9), maxlength(x10)), N, maxlength(x1), maxlength(x2), maxlength(x3), maxlength(x4), maxlength(x5), maxlength(x6), maxlength(x7), maxlength(x8), maxlength(x9), maxlength(x10)))=> foreach i2 <= N2 do foreach i <= N do Of(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7, x8:input8, x9:input9, x10:input10) := find[unique] j<=N suchthat defined(x1[j], x2[j], x3[j], x4[j], x5[j], x6[j], x7[j], x8[j], x9[j], x10[j],r[j]) && (x1 = x1[j]) && (x2 = x2[j]) && (x3 = x3[j]) && (x4 = x4[j]) && (x5 = x5[j]) && (x6 = x6[j]) && (x7 = x7[j]) && (x8 = x8[j]) && (x9 = x9[j]) && (x10 = x10[j]) then return(r[j]) else r <-R output; return(r). } 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) { param N, Ncoll, Ncoll2, N2. fun f(key, input1): output. equiv(prf(f)) foreach i2 <= N2 do k <-R key; (foreach i <= N do Of(x1:input1) := return(f(k, x1)) | foreach icoll <= Ncoll do Ofcoll(x'1:input1, r': output) := return(f(k, x'1) = r') | foreach icoll2 <= Ncoll2 do Ofcoll2(y1:input1, z1:input1) := return(f(k, y1) = f(k, z1))) <=(N2 * (Pprf(time + (N2-1)*(N+Ncoll+2*Ncoll2)*time(f, max(maxlength(x1), maxlength(x'1), maxlength(y1), maxlength(z1))), N + Ncoll + 2*Ncoll2, max(maxlength(x1), maxlength(x'1), maxlength(y1), maxlength(z1))) + Ncoll * Pcoll1rand(output) + Ncoll2 * Pcoll2rand(output)))=> foreach i2 <= N2 do (foreach i <= N do Of(x1:input1) := find[unique] j<=N suchthat defined(x1[j],r[j]) && (x1 = x1[j]) then return(r[j]) else r <-R output; return(r) | foreach icoll <= Ncoll do Ofcoll(x'1:input1, r': output) := find[unique] j<=N suchthat defined(x1[j],r[j]) && (x'1 = x1[j]) then return(r[j] = r') else return(false) | foreach icoll2 <= Ncoll2 do Ofcoll2(y1:input1, z1:input1) := return((y1 = z1))). } def PRF_large_2(key, input1, input2, output, f, Pprf) { param N, Ncoll, Ncoll2, N2. fun f(key, input1, input2): output. equiv(prf(f)) foreach i2 <= N2 do k <-R key; (foreach i <= N do Of(x1:input1, x2:input2) := return(f(k, x1, x2)) | foreach icoll <= Ncoll do Ofcoll(x'1:input1, x'2:input2, r': output) := return(f(k, x'1, x'2) = r') | foreach icoll2 <= Ncoll2 do Ofcoll2(y1:input1, y2:input2, z1:input1, z2:input2) := return(f(k, y1, y2) = f(k, z1, z2))) <=(N2 * (Pprf(time + (N2-1)*(N+Ncoll+2*Ncoll2)*time(f, max(maxlength(x1), maxlength(x'1), maxlength(y1), maxlength(z1)), max(maxlength(x2), maxlength(x'2), maxlength(y2), maxlength(z2))), N + Ncoll + 2*Ncoll2, max(maxlength(x1), maxlength(x'1), maxlength(y1), maxlength(z1)), max(maxlength(x2), maxlength(x'2), maxlength(y2), maxlength(z2))) + Ncoll * Pcoll1rand(output) + Ncoll2 * Pcoll2rand(output)))=> foreach i2 <= N2 do (foreach i <= N do Of(x1:input1, x2:input2) := find[unique] j<=N suchthat defined(x1[j], x2[j],r[j]) && (x1 = x1[j]) && (x2 = x2[j]) then return(r[j]) else r <-R output; return(r) | foreach icoll <= Ncoll do Ofcoll(x'1:input1, x'2:input2, r': output) := find[unique] j<=N suchthat defined(x1[j], x2[j],r[j]) && (x'1 = x1[j]) && (x'2 = x2[j]) then return(r[j] = r') else return(false) | foreach icoll2 <= Ncoll2 do Ofcoll2(y1:input1, y2:input2, z1:input1, z2:input2) := return((y1 = z1) && (y2 = z2))). } def PRF_large_3(key, input1, input2, input3, output, f, Pprf) { param N, Ncoll, Ncoll2, N2. fun f(key, input1, input2, input3): output. equiv(prf(f)) foreach i2 <= N2 do k <-R key; (foreach i <= N do Of(x1:input1, x2:input2, x3:input3) := return(f(k, x1, x2, x3)) | foreach icoll <= Ncoll do Ofcoll(x'1:input1, x'2:input2, x'3:input3, r': output) := return(f(k, x'1, x'2, x'3) = r') | foreach icoll2 <= Ncoll2 do Ofcoll2(y1:input1, y2:input2, y3:input3, z1:input1, z2:input2, z3:input3) := return(f(k, y1, y2, y3) = f(k, z1, z2, z3))) <=(N2 * (Pprf(time + (N2-1)*(N+Ncoll+2*Ncoll2)*time(f, max(maxlength(x1), maxlength(x'1), maxlength(y1), maxlength(z1)), max(maxlength(x2), maxlength(x'2), maxlength(y2), maxlength(z2)), max(maxlength(x3), maxlength(x'3), maxlength(y3), maxlength(z3))), N + Ncoll + 2*Ncoll2, max(maxlength(x1), maxlength(x'1), maxlength(y1), maxlength(z1)), max(maxlength(x2), maxlength(x'2), maxlength(y2), maxlength(z2)), max(maxlength(x3), maxlength(x'3), maxlength(y3), maxlength(z3))) + Ncoll * Pcoll1rand(output) + Ncoll2 * Pcoll2rand(output)))=> foreach i2 <= N2 do (foreach i <= N do Of(x1:input1, x2:input2, x3:input3) := find[unique] j<=N suchthat defined(x1[j], x2[j], x3[j],r[j]) && (x1 = x1[j]) && (x2 = x2[j]) && (x3 = x3[j]) then return(r[j]) else r <-R output; return(r) | foreach icoll <= Ncoll do Ofcoll(x'1:input1, x'2:input2, x'3:input3, r': output) := find[unique] j<=N suchthat defined(x1[j], x2[j], x3[j],r[j]) && (x'1 = x1[j]) && (x'2 = x2[j]) && (x'3 = x3[j]) then return(r[j] = r') else return(false) | foreach icoll2 <= Ncoll2 do Ofcoll2(y1:input1, y2:input2, y3:input3, z1:input1, z2:input2, z3:input3) := return((y1 = z1) && (y2 = z2) && (y3 = z3))). } def PRF_large_4(key, input1, input2, input3, input4, output, f, Pprf) { param N, Ncoll, Ncoll2, N2. fun f(key, input1, input2, input3, input4): output. equiv(prf(f)) foreach i2 <= N2 do k <-R key; (foreach i <= N do Of(x1:input1, x2:input2, x3:input3, x4:input4) := return(f(k, x1, x2, x3, x4)) | foreach icoll <= Ncoll do Ofcoll(x'1:input1, x'2:input2, x'3:input3, x'4:input4, r': output) := return(f(k, x'1, x'2, x'3, x'4) = r') | foreach icoll2 <= Ncoll2 do Ofcoll2(y1:input1, y2:input2, y3:input3, y4:input4, z1:input1, z2:input2, z3:input3, z4:input4) := return(f(k, y1, y2, y3, y4) = f(k, z1, z2, z3, z4))) <=(N2 * (Pprf(time + (N2-1)*(N+Ncoll+2*Ncoll2)*time(f, max(maxlength(x1), maxlength(x'1), maxlength(y1), maxlength(z1)), max(maxlength(x2), maxlength(x'2), maxlength(y2), maxlength(z2)), max(maxlength(x3), maxlength(x'3), maxlength(y3), maxlength(z3)), max(maxlength(x4), maxlength(x'4), maxlength(y4), maxlength(z4))), N + Ncoll + 2*Ncoll2, max(maxlength(x1), maxlength(x'1), maxlength(y1), maxlength(z1)), max(maxlength(x2), maxlength(x'2), maxlength(y2), maxlength(z2)), max(maxlength(x3), maxlength(x'3), maxlength(y3), maxlength(z3)), max(maxlength(x4), maxlength(x'4), maxlength(y4), maxlength(z4))) + Ncoll * Pcoll1rand(output) + Ncoll2 * Pcoll2rand(output)))=> foreach i2 <= N2 do (foreach i <= N do Of(x1:input1, x2:input2, x3:input3, x4:input4) := find[unique] j<=N suchthat defined(x1[j], x2[j], x3[j], x4[j],r[j]) && (x1 = x1[j]) && (x2 = x2[j]) && (x3 = x3[j]) && (x4 = x4[j]) then return(r[j]) else r <-R output; return(r) | foreach icoll <= Ncoll do Ofcoll(x'1:input1, x'2:input2, x'3:input3, x'4:input4, r': output) := find[unique] j<=N suchthat defined(x1[j], x2[j], x3[j], x4[j],r[j]) && (x'1 = x1[j]) && (x'2 = x2[j]) && (x'3 = x3[j]) && (x'4 = x4[j]) then return(r[j] = r') else return(false) | foreach icoll2 <= Ncoll2 do Ofcoll2(y1:input1, y2:input2, y3:input3, y4:input4, z1:input1, z2:input2, z3:input3, z4:input4) := return((y1 = z1) && (y2 = z2) && (y3 = z3) && (y4 = z4))). } def PRF_large_5(key, input1, input2, input3, input4, input5, output, f, Pprf) { param N, Ncoll, Ncoll2, N2. fun f(key, input1, input2, input3, input4, input5): output. equiv(prf(f)) foreach i2 <= N2 do k <-R key; (foreach i <= N do Of(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5) := return(f(k, x1, x2, x3, x4, x5)) | foreach icoll <= Ncoll do Ofcoll(x'1:input1, x'2:input2, x'3:input3, x'4:input4, x'5:input5, r': output) := return(f(k, x'1, x'2, x'3, x'4, x'5) = r') | foreach icoll2 <= Ncoll2 do Ofcoll2(y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, z1:input1, z2:input2, z3:input3, z4:input4, z5:input5) := return(f(k, y1, y2, y3, y4, y5) = f(k, z1, z2, z3, z4, z5))) <=(N2 * (Pprf(time + (N2-1)*(N+Ncoll+2*Ncoll2)*time(f, max(maxlength(x1), maxlength(x'1), maxlength(y1), maxlength(z1)), max(maxlength(x2), maxlength(x'2), maxlength(y2), maxlength(z2)), max(maxlength(x3), maxlength(x'3), maxlength(y3), maxlength(z3)), max(maxlength(x4), maxlength(x'4), maxlength(y4), maxlength(z4)), max(maxlength(x5), maxlength(x'5), maxlength(y5), maxlength(z5))), N + Ncoll + 2*Ncoll2, max(maxlength(x1), maxlength(x'1), maxlength(y1), maxlength(z1)), max(maxlength(x2), maxlength(x'2), maxlength(y2), maxlength(z2)), max(maxlength(x3), maxlength(x'3), maxlength(y3), maxlength(z3)), max(maxlength(x4), maxlength(x'4), maxlength(y4), maxlength(z4)), max(maxlength(x5), maxlength(x'5), maxlength(y5), maxlength(z5))) + Ncoll * Pcoll1rand(output) + Ncoll2 * Pcoll2rand(output)))=> foreach i2 <= N2 do (foreach i <= N do Of(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5) := find[unique] j<=N suchthat defined(x1[j], x2[j], x3[j], x4[j], x5[j],r[j]) && (x1 = x1[j]) && (x2 = x2[j]) && (x3 = x3[j]) && (x4 = x4[j]) && (x5 = x5[j]) then return(r[j]) else r <-R output; return(r) | foreach icoll <= Ncoll do Ofcoll(x'1:input1, x'2:input2, x'3:input3, x'4:input4, x'5:input5, r': output) := find[unique] j<=N suchthat defined(x1[j], x2[j], x3[j], x4[j], x5[j],r[j]) && (x'1 = x1[j]) && (x'2 = x2[j]) && (x'3 = x3[j]) && (x'4 = x4[j]) && (x'5 = x5[j]) then return(r[j] = r') else return(false) | foreach icoll2 <= Ncoll2 do Ofcoll2(y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, z1:input1, z2:input2, z3:input3, z4:input4, z5:input5) := return((y1 = z1) && (y2 = z2) && (y3 = z3) && (y4 = z4) && (y5 = z5))). } def PRF_large_6(key, input1, input2, input3, input4, input5, input6, output, f, Pprf) { param N, Ncoll, Ncoll2, N2. fun f(key, input1, input2, input3, input4, input5, input6): output. equiv(prf(f)) foreach i2 <= N2 do k <-R key; (foreach i <= N do Of(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6) := return(f(k, x1, x2, x3, x4, x5, x6)) | foreach icoll <= Ncoll do Ofcoll(x'1:input1, x'2:input2, x'3:input3, x'4:input4, x'5:input5, x'6:input6, r': output) := return(f(k, x'1, x'2, x'3, x'4, x'5, x'6) = r') | foreach icoll2 <= Ncoll2 do Ofcoll2(y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6) := return(f(k, y1, y2, y3, y4, y5, y6) = f(k, z1, z2, z3, z4, z5, z6))) <=(N2 * (Pprf(time + (N2-1)*(N+Ncoll+2*Ncoll2)*time(f, max(maxlength(x1), maxlength(x'1), maxlength(y1), maxlength(z1)), max(maxlength(x2), maxlength(x'2), maxlength(y2), maxlength(z2)), max(maxlength(x3), maxlength(x'3), maxlength(y3), maxlength(z3)), max(maxlength(x4), maxlength(x'4), maxlength(y4), maxlength(z4)), max(maxlength(x5), maxlength(x'5), maxlength(y5), maxlength(z5)), max(maxlength(x6), maxlength(x'6), maxlength(y6), maxlength(z6))), N + Ncoll + 2*Ncoll2, max(maxlength(x1), maxlength(x'1), maxlength(y1), maxlength(z1)), max(maxlength(x2), maxlength(x'2), maxlength(y2), maxlength(z2)), max(maxlength(x3), maxlength(x'3), maxlength(y3), maxlength(z3)), max(maxlength(x4), maxlength(x'4), maxlength(y4), maxlength(z4)), max(maxlength(x5), maxlength(x'5), maxlength(y5), maxlength(z5)), max(maxlength(x6), maxlength(x'6), maxlength(y6), maxlength(z6))) + Ncoll * Pcoll1rand(output) + Ncoll2 * Pcoll2rand(output)))=> foreach i2 <= N2 do (foreach i <= N do Of(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6) := find[unique] j<=N suchthat defined(x1[j], x2[j], x3[j], x4[j], x5[j], x6[j],r[j]) && (x1 = x1[j]) && (x2 = x2[j]) && (x3 = x3[j]) && (x4 = x4[j]) && (x5 = x5[j]) && (x6 = x6[j]) then return(r[j]) else r <-R output; return(r) | foreach icoll <= Ncoll do Ofcoll(x'1:input1, x'2:input2, x'3:input3, x'4:input4, x'5:input5, x'6:input6, r': output) := find[unique] j<=N suchthat defined(x1[j], x2[j], x3[j], x4[j], x5[j], x6[j],r[j]) && (x'1 = x1[j]) && (x'2 = x2[j]) && (x'3 = x3[j]) && (x'4 = x4[j]) && (x'5 = x5[j]) && (x'6 = x6[j]) then return(r[j] = r') else return(false) | foreach icoll2 <= Ncoll2 do Ofcoll2(y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6) := return((y1 = z1) && (y2 = z2) && (y3 = z3) && (y4 = z4) && (y5 = z5) && (y6 = z6))). } def PRF_large_7(key, input1, input2, input3, input4, input5, input6, input7, output, f, Pprf) { param N, Ncoll, Ncoll2, N2. fun f(key, input1, input2, input3, input4, input5, input6, input7): output. equiv(prf(f)) foreach i2 <= N2 do k <-R key; (foreach i <= N do Of(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 icoll <= Ncoll do Ofcoll(x'1:input1, x'2:input2, x'3:input3, x'4:input4, x'5:input5, x'6:input6, x'7:input7, r': output) := return(f(k, x'1, x'2, x'3, x'4, x'5, x'6, x'7) = r') | foreach icoll2 <= Ncoll2 do Ofcoll2(y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6, z7:input7) := return(f(k, y1, y2, y3, y4, y5, y6, y7) = f(k, z1, z2, z3, z4, z5, z6, z7))) <=(N2 * (Pprf(time + (N2-1)*(N+Ncoll+2*Ncoll2)*time(f, max(maxlength(x1), maxlength(x'1), maxlength(y1), maxlength(z1)), max(maxlength(x2), maxlength(x'2), maxlength(y2), maxlength(z2)), max(maxlength(x3), maxlength(x'3), maxlength(y3), maxlength(z3)), max(maxlength(x4), maxlength(x'4), maxlength(y4), maxlength(z4)), max(maxlength(x5), maxlength(x'5), maxlength(y5), maxlength(z5)), max(maxlength(x6), maxlength(x'6), maxlength(y6), maxlength(z6)), max(maxlength(x7), maxlength(x'7), maxlength(y7), maxlength(z7))), N + Ncoll + 2*Ncoll2, max(maxlength(x1), maxlength(x'1), maxlength(y1), maxlength(z1)), max(maxlength(x2), maxlength(x'2), maxlength(y2), maxlength(z2)), max(maxlength(x3), maxlength(x'3), maxlength(y3), maxlength(z3)), max(maxlength(x4), maxlength(x'4), maxlength(y4), maxlength(z4)), max(maxlength(x5), maxlength(x'5), maxlength(y5), maxlength(z5)), max(maxlength(x6), maxlength(x'6), maxlength(y6), maxlength(z6)), max(maxlength(x7), maxlength(x'7), maxlength(y7), maxlength(z7))) + Ncoll * Pcoll1rand(output) + Ncoll2 * Pcoll2rand(output)))=> foreach i2 <= N2 do (foreach i <= N do Of(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7) := find[unique] j<=N suchthat defined(x1[j], x2[j], x3[j], x4[j], x5[j], x6[j], x7[j],r[j]) && (x1 = x1[j]) && (x2 = x2[j]) && (x3 = x3[j]) && (x4 = x4[j]) && (x5 = x5[j]) && (x6 = x6[j]) && (x7 = x7[j]) then return(r[j]) else r <-R output; return(r) | foreach icoll <= Ncoll do Ofcoll(x'1:input1, x'2:input2, x'3:input3, x'4:input4, x'5:input5, x'6:input6, x'7:input7, r': output) := find[unique] j<=N suchthat defined(x1[j], x2[j], x3[j], x4[j], x5[j], x6[j], x7[j],r[j]) && (x'1 = x1[j]) && (x'2 = x2[j]) && (x'3 = x3[j]) && (x'4 = x4[j]) && (x'5 = x5[j]) && (x'6 = x6[j]) && (x'7 = x7[j]) then return(r[j] = r') else return(false) | foreach icoll2 <= Ncoll2 do Ofcoll2(y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6, z7:input7) := return((y1 = z1) && (y2 = z2) && (y3 = z3) && (y4 = z4) && (y5 = z5) && (y6 = z6) && (y7 = z7))). } def PRF_large_8(key, input1, input2, input3, input4, input5, input6, input7, input8, output, f, Pprf) { param N, Ncoll, Ncoll2, N2. fun f(key, input1, input2, input3, input4, input5, input6, input7, input8): output. equiv(prf(f)) foreach i2 <= N2 do k <-R key; (foreach i <= N do Of(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 icoll <= Ncoll do Ofcoll(x'1:input1, x'2:input2, x'3:input3, x'4:input4, x'5:input5, x'6:input6, x'7:input7, x'8:input8, r': output) := return(f(k, x'1, x'2, x'3, x'4, x'5, x'6, x'7, x'8) = r') | foreach icoll2 <= Ncoll2 do Ofcoll2(y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8, z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6, z7:input7, z8:input8) := return(f(k, y1, y2, y3, y4, y5, y6, y7, y8) = f(k, z1, z2, z3, z4, z5, z6, z7, z8))) <=(N2 * (Pprf(time + (N2-1)*(N+Ncoll+2*Ncoll2)*time(f, max(maxlength(x1), maxlength(x'1), maxlength(y1), maxlength(z1)), max(maxlength(x2), maxlength(x'2), maxlength(y2), maxlength(z2)), max(maxlength(x3), maxlength(x'3), maxlength(y3), maxlength(z3)), max(maxlength(x4), maxlength(x'4), maxlength(y4), maxlength(z4)), max(maxlength(x5), maxlength(x'5), maxlength(y5), maxlength(z5)), max(maxlength(x6), maxlength(x'6), maxlength(y6), maxlength(z6)), max(maxlength(x7), maxlength(x'7), maxlength(y7), maxlength(z7)), max(maxlength(x8), maxlength(x'8), maxlength(y8), maxlength(z8))), N + Ncoll + 2*Ncoll2, max(maxlength(x1), maxlength(x'1), maxlength(y1), maxlength(z1)), max(maxlength(x2), maxlength(x'2), maxlength(y2), maxlength(z2)), max(maxlength(x3), maxlength(x'3), maxlength(y3), maxlength(z3)), max(maxlength(x4), maxlength(x'4), maxlength(y4), maxlength(z4)), max(maxlength(x5), maxlength(x'5), maxlength(y5), maxlength(z5)), max(maxlength(x6), maxlength(x'6), maxlength(y6), maxlength(z6)), max(maxlength(x7), maxlength(x'7), maxlength(y7), maxlength(z7)), max(maxlength(x8), maxlength(x'8), maxlength(y8), maxlength(z8))) + Ncoll * Pcoll1rand(output) + Ncoll2 * Pcoll2rand(output)))=> foreach i2 <= N2 do (foreach i <= N do Of(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7, x8:input8) := find[unique] j<=N suchthat defined(x1[j], x2[j], x3[j], x4[j], x5[j], x6[j], x7[j], x8[j],r[j]) && (x1 = x1[j]) && (x2 = x2[j]) && (x3 = x3[j]) && (x4 = x4[j]) && (x5 = x5[j]) && (x6 = x6[j]) && (x7 = x7[j]) && (x8 = x8[j]) then return(r[j]) else r <-R output; return(r) | foreach icoll <= Ncoll do Ofcoll(x'1:input1, x'2:input2, x'3:input3, x'4:input4, x'5:input5, x'6:input6, x'7:input7, x'8:input8, r': output) := find[unique] j<=N suchthat defined(x1[j], x2[j], x3[j], x4[j], x5[j], x6[j], x7[j], x8[j],r[j]) && (x'1 = x1[j]) && (x'2 = x2[j]) && (x'3 = x3[j]) && (x'4 = x4[j]) && (x'5 = x5[j]) && (x'6 = x6[j]) && (x'7 = x7[j]) && (x'8 = x8[j]) then return(r[j] = r') else return(false) | foreach icoll2 <= Ncoll2 do Ofcoll2(y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8, z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6, z7:input7, z8:input8) := return((y1 = z1) && (y2 = z2) && (y3 = z3) && (y4 = z4) && (y5 = z5) && (y6 = z6) && (y7 = z7) && (y8 = z8))). } def PRF_large_9(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, output, f, Pprf) { param N, Ncoll, Ncoll2, N2. fun f(key, input1, input2, input3, input4, input5, input6, input7, input8, input9): output. equiv(prf(f)) foreach i2 <= N2 do k <-R key; (foreach i <= N do Of(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 icoll <= Ncoll do Ofcoll(x'1:input1, x'2:input2, x'3:input3, x'4:input4, x'5:input5, x'6:input6, x'7:input7, x'8:input8, x'9:input9, r': output) := return(f(k, x'1, x'2, x'3, x'4, x'5, x'6, x'7, x'8, x'9) = r') | foreach icoll2 <= Ncoll2 do Ofcoll2(y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8, y9:input9, z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6, z7:input7, z8:input8, z9:input9) := return(f(k, y1, y2, y3, y4, y5, y6, y7, y8, y9) = f(k, z1, z2, z3, z4, z5, z6, z7, z8, z9))) <=(N2 * (Pprf(time + (N2-1)*(N+Ncoll+2*Ncoll2)*time(f, max(maxlength(x1), maxlength(x'1), maxlength(y1), maxlength(z1)), max(maxlength(x2), maxlength(x'2), maxlength(y2), maxlength(z2)), max(maxlength(x3), maxlength(x'3), maxlength(y3), maxlength(z3)), max(maxlength(x4), maxlength(x'4), maxlength(y4), maxlength(z4)), max(maxlength(x5), maxlength(x'5), maxlength(y5), maxlength(z5)), max(maxlength(x6), maxlength(x'6), maxlength(y6), maxlength(z6)), max(maxlength(x7), maxlength(x'7), maxlength(y7), maxlength(z7)), max(maxlength(x8), maxlength(x'8), maxlength(y8), maxlength(z8)), max(maxlength(x9), maxlength(x'9), maxlength(y9), maxlength(z9))), N + Ncoll + 2*Ncoll2, max(maxlength(x1), maxlength(x'1), maxlength(y1), maxlength(z1)), max(maxlength(x2), maxlength(x'2), maxlength(y2), maxlength(z2)), max(maxlength(x3), maxlength(x'3), maxlength(y3), maxlength(z3)), max(maxlength(x4), maxlength(x'4), maxlength(y4), maxlength(z4)), max(maxlength(x5), maxlength(x'5), maxlength(y5), maxlength(z5)), max(maxlength(x6), maxlength(x'6), maxlength(y6), maxlength(z6)), max(maxlength(x7), maxlength(x'7), maxlength(y7), maxlength(z7)), max(maxlength(x8), maxlength(x'8), maxlength(y8), maxlength(z8)), max(maxlength(x9), maxlength(x'9), maxlength(y9), maxlength(z9))) + Ncoll * Pcoll1rand(output) + Ncoll2 * Pcoll2rand(output)))=> foreach i2 <= N2 do (foreach i <= N do Of(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7, x8:input8, x9:input9) := find[unique] j<=N suchthat defined(x1[j], x2[j], x3[j], x4[j], x5[j], x6[j], x7[j], x8[j], x9[j],r[j]) && (x1 = x1[j]) && (x2 = x2[j]) && (x3 = x3[j]) && (x4 = x4[j]) && (x5 = x5[j]) && (x6 = x6[j]) && (x7 = x7[j]) && (x8 = x8[j]) && (x9 = x9[j]) then return(r[j]) else r <-R output; return(r) | foreach icoll <= Ncoll do Ofcoll(x'1:input1, x'2:input2, x'3:input3, x'4:input4, x'5:input5, x'6:input6, x'7:input7, x'8:input8, x'9:input9, r': output) := find[unique] j<=N suchthat defined(x1[j], x2[j], x3[j], x4[j], x5[j], x6[j], x7[j], x8[j], x9[j],r[j]) && (x'1 = x1[j]) && (x'2 = x2[j]) && (x'3 = x3[j]) && (x'4 = x4[j]) && (x'5 = x5[j]) && (x'6 = x6[j]) && (x'7 = x7[j]) && (x'8 = x8[j]) && (x'9 = x9[j]) then return(r[j] = r') else return(false) | foreach icoll2 <= Ncoll2 do Ofcoll2(y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8, y9:input9, z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6, z7:input7, z8:input8, z9:input9) := return((y1 = z1) && (y2 = z2) && (y3 = z3) && (y4 = z4) && (y5 = z5) && (y6 = z6) && (y7 = z7) && (y8 = z8) && (y9 = z9))). } def PRF_large_10(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10, output, f, Pprf) { param N, Ncoll, Ncoll2, N2. fun f(key, input1, input2, input3, input4, input5, input6, input7, input8, input9, input10): output. equiv(prf(f)) foreach i2 <= N2 do k <-R key; (foreach i <= N do Of(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 icoll <= Ncoll do Ofcoll(x'1:input1, x'2:input2, x'3:input3, x'4:input4, x'5:input5, x'6:input6, x'7:input7, x'8:input8, x'9:input9, x'10:input10, r': output) := return(f(k, x'1, x'2, x'3, x'4, x'5, x'6, x'7, x'8, x'9, x'10) = r') | foreach icoll2 <= Ncoll2 do Ofcoll2(y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8, y9:input9, y10:input10, z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6, z7:input7, z8:input8, z9:input9, z10:input10) := return(f(k, y1, y2, y3, y4, y5, y6, y7, y8, y9, y10) = f(k, z1, z2, z3, z4, z5, z6, z7, z8, z9, z10))) <=(N2 * (Pprf(time + (N2-1)*(N+Ncoll+2*Ncoll2)*time(f, max(maxlength(x1), maxlength(x'1), maxlength(y1), maxlength(z1)), max(maxlength(x2), maxlength(x'2), maxlength(y2), maxlength(z2)), max(maxlength(x3), maxlength(x'3), maxlength(y3), maxlength(z3)), max(maxlength(x4), maxlength(x'4), maxlength(y4), maxlength(z4)), max(maxlength(x5), maxlength(x'5), maxlength(y5), maxlength(z5)), max(maxlength(x6), maxlength(x'6), maxlength(y6), maxlength(z6)), max(maxlength(x7), maxlength(x'7), maxlength(y7), maxlength(z7)), max(maxlength(x8), maxlength(x'8), maxlength(y8), maxlength(z8)), max(maxlength(x9), maxlength(x'9), maxlength(y9), maxlength(z9)), max(maxlength(x10), maxlength(x'10), maxlength(y10), maxlength(z10))), N + Ncoll + 2*Ncoll2, max(maxlength(x1), maxlength(x'1), maxlength(y1), maxlength(z1)), max(maxlength(x2), maxlength(x'2), maxlength(y2), maxlength(z2)), max(maxlength(x3), maxlength(x'3), maxlength(y3), maxlength(z3)), max(maxlength(x4), maxlength(x'4), maxlength(y4), maxlength(z4)), max(maxlength(x5), maxlength(x'5), maxlength(y5), maxlength(z5)), max(maxlength(x6), maxlength(x'6), maxlength(y6), maxlength(z6)), max(maxlength(x7), maxlength(x'7), maxlength(y7), maxlength(z7)), max(maxlength(x8), maxlength(x'8), maxlength(y8), maxlength(z8)), max(maxlength(x9), maxlength(x'9), maxlength(y9), maxlength(z9)), max(maxlength(x10), maxlength(x'10), maxlength(y10), maxlength(z10))) + Ncoll * Pcoll1rand(output) + Ncoll2 * Pcoll2rand(output)))=> foreach i2 <= N2 do (foreach i <= N do Of(x1:input1, x2:input2, x3:input3, x4:input4, x5:input5, x6:input6, x7:input7, x8:input8, x9:input9, x10:input10) := find[unique] j<=N suchthat defined(x1[j], x2[j], x3[j], x4[j], x5[j], x6[j], x7[j], x8[j], x9[j], x10[j],r[j]) && (x1 = x1[j]) && (x2 = x2[j]) && (x3 = x3[j]) && (x4 = x4[j]) && (x5 = x5[j]) && (x6 = x6[j]) && (x7 = x7[j]) && (x8 = x8[j]) && (x9 = x9[j]) && (x10 = x10[j]) then return(r[j]) else r <-R output; return(r) | foreach icoll <= Ncoll do Ofcoll(x'1:input1, x'2:input2, x'3:input3, x'4:input4, x'5:input5, x'6:input6, x'7:input7, x'8:input8, x'9:input9, x'10:input10, r': output) := find[unique] j<=N suchthat defined(x1[j], x2[j], x3[j], x4[j], x5[j], x6[j], x7[j], x8[j], x9[j], x10[j],r[j]) && (x'1 = x1[j]) && (x'2 = x2[j]) && (x'3 = x3[j]) && (x'4 = x4[j]) && (x'5 = x5[j]) && (x'6 = x6[j]) && (x'7 = x7[j]) && (x'8 = x8[j]) && (x'9 = x9[j]) && (x'10 = x10[j]) then return(r[j] = r') else return(false) | foreach icoll2 <= Ncoll2 do Ofcoll2(y1:input1, y2:input2, y3:input3, y4:input4, y5:input5, y6:input6, y7:input7, y8:input8, y9:input9, y10:input10, z1:input1, z2:input2, z3:input3, z4:input4, z5:input5, z6:input6, z7:input7, z8:input8, z9:input9, z10:input10) := return((y1 = z1) && (y2 = z2) && (y3 = z3) && (y4 = z4) && (y5 = z5) && (y6 = z6) && (y7 = z7) && (y8 = z8) && (y9 = z9) && (y10 = z10))). } 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) { param Ne, Nd, Necoll, Ndcoll, Nck. 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)) foreach ick <= Nck do ck <-R cipherkey; (foreach ie <= Ne do Oenc(me:blocksize, ke:key) := return(enc(ck, me, ke)) | foreach id <= Nd do Odec(md:blocksize, kd:key) := return(dec(ck, md, kd)) | foreach iecoll <= Necoll do Oenccoll(me':blocksize, ke':key, re': blocksize) := return(enc(ck, me', ke') = re') | foreach idcoll <= Ndcoll do Odeccoll(md':blocksize, kd':key, rd': blocksize) := return(dec(ck, md', kd') = rd')) <=((#Oenc+#Odec)*(#Oenc+#Odec-1)*Pcoll2rand(blocksize) + (#Oenccoll + #Odeccoll) * Pcoll1rand(blocksize))=> foreach ick <= Nck do (foreach ie <= Ne do Oenc(me:blocksize, ke:key) := find[unique] j<=Ne suchthat defined(me[j],ke[j],re[j]) && me = me[j] && ke = ke[j] then return(re[j]) orfind k<=Nd suchthat defined(rd[k],md[k],kd[k]) && me = rd[k] && ke = kd[k] then return(md[k]) else re <-R blocksize; return(re) | foreach id <= Nd do Odec(md:blocksize, kd:key) := find[unique] j<=Ne suchthat defined(me[j],ke[j],re[j]) && md = re[j] && kd = ke[j] then return(me[j]) orfind k<=Nd suchthat defined(rd[k],md[k],kd[k]) && md = md[k] && kd = kd[k] then return(rd[k]) else rd <-R blocksize; return(rd) | foreach iecoll <= Necoll do Oenccoll(me':blocksize, ke':key, re': blocksize) := find[unique] j<=Ne suchthat defined(me[j],ke[j],re[j]) && me' = me[j] && ke' = ke[j] then return(re' = re[j]) orfind k<=Nd suchthat defined(rd[k],md[k],kd[k]) && me' = rd[k] && ke' = kd[k] then return(re' = md[k]) else return(false) | foreach idcoll <= Ndcoll do Odeccoll(md':blocksize, kd':key, rd': blocksize) := find[unique] j<=Ne suchthat defined(me[j],ke[j],re[j]) && md' = re[j] && kd' = ke[j] then return(rd' = me[j]) orfind k<=Nd suchthat defined(rd[k],md[k],kd[k]) && md' = md[k] && kd' = kd[k] then return(rd' = rd[k]) else return(false)). (* 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]. channel chE1, chE2, chD1, chD2. let enc_dec_oracle(ck: cipherkey) = (foreach iE <= qE do in(chE1, (x:blocksize, ke:key)); out(chE2, enc(ck,x,ke))) | (foreach iD <= qD do in(chD1, (m:blocksize, kd:key)); out(chD2, 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)). param N. equiv(splitter(split)) foreach i <= N do r <-R input_t; (O1() := return(get1(r))) <=(0)=> foreach i <= N do 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)). param N. equiv(splitter(split)) foreach i <= N do r <-R input_t; (O1() := return(get1(r)) | O2() := return(get2(r))) <=(0)=> foreach i <= N do 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)). param N. equiv(splitter(split)) foreach i <= N do r <-R input_t; (O1() := return(get1(r)) | O2() := return(get2(r)) | O3() := return(get3(r))) <=(0)=> foreach i <= N do 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)). param N. equiv(splitter(split)) foreach i <= N do r <-R input_t; (O1() := return(get1(r)) | O2() := return(get2(r)) | O3() := return(get3(r)) | O4() := return(get4(r))) <=(0)=> foreach i <= N do 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)). param N. equiv(splitter(split)) foreach i <= N do 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)=> foreach i <= N do 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)). param N. equiv(splitter(split)) foreach i <= N do 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)=> foreach i <= N do 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)). param N. equiv(splitter(split)) foreach i <= N do 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)=> foreach i <= N do 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)). param N. equiv(splitter(split)) foreach i <= N do 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)=> foreach i <= N do 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)). param N. equiv(splitter(split)) foreach i <= N do 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)=> foreach i <= N do 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)). param N. equiv(splitter(split)) foreach i <= N do 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)=> foreach i <= N do 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)). } (*********************************************************************** End of default.cvl shipped with CryptoVerif ***********************************************************************) (******************************* 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" hashinput%: type of the %-th input of the hash function hashoutput: type of the output of the hash function, must be "bounded" or "nonuniform" (typically "fixed"), and "large". hash: the hash function. WARNING: hash 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 hashoracle(k) where k is the key. qH is the number of calls to hashoracle. The types key, hashinput%, and hashoutput must be declared before this macro. The function hash, the process hashoracle, 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. ADDED ARGUMENTS: r: variable containing the random result of the hash function x%: variables containing the arguments of the hash function ch1, ch2: channels for input/output in the hash oracle. *) def ROM_hash_refactored_1(key, hashinput1, hashoutput, hash, hashoracle, r, x1, ch1, ch2, qH) { param Nh, N, Neq, Ncoll. fun hash(key, hashinput1):hashoutput. equiv(rom(hash)) foreach ih <= Nh do k <-R key; (foreach i <= N do OH(x1: hashinput1) := return(hash(k, x1)) | foreach ieq <= Neq do Oeq(x1': hashinput1, r': hashoutput) := return(r' = hash(k, x1')) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, z1: hashinput1) := return(hash(k, y1) = hash(k, z1))) <=(#Oeq * Pcoll1rand(hashoutput) + #Ocoll * Pcoll2rand(hashoutput))=> foreach ih <= Nh do (foreach i <= N do OH(x1: hashinput1) := find[unique] u <= N suchthat defined(x1[u], r[u]) && x1 = x1[u] then return(r[u]) else r <-R hashoutput; return(r) | foreach ieq <= Neq do Oeq(x1': hashinput1, r': hashoutput) := find[unique] u <= N suchthat defined(x1[u], r[u]) && x1' = x1[u] then return(r' = r[u]) else return(false) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, z1: hashinput1) := return(y1 = z1)). param qH [noninteractive]. channel ch1, ch2. let hashoracle(k: key) = foreach iH <= qH do in(ch1, (x1: hashinput1)); out(ch2, hash(k, x1)). } def ROM_hash_refactored_2(key, hashinput1, hashinput2, hashoutput, hash, hashoracle, r, x1, x2, ch1, ch2, qH) { param Nh, N, Neq, Ncoll. fun hash(key, hashinput1, hashinput2):hashoutput. equiv(rom(hash)) foreach ih <= Nh do k <-R key; (foreach i <= N do OH(x1: hashinput1, x2: hashinput2) := return(hash(k, x1, x2)) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, r': hashoutput) := return(r' = hash(k, x1', x2')) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, z1: hashinput1, z2: hashinput2) := return(hash(k, y1, y2) = hash(k, z1, z2))) <=(#Oeq * Pcoll1rand(hashoutput) + #Ocoll * Pcoll2rand(hashoutput))=> foreach ih <= Nh do (foreach i <= N do OH(x1: hashinput1, x2: hashinput2) := find[unique] u <= N suchthat defined(x1[u], x2[u], r[u]) && x1 = x1[u] && x2 = x2[u] then return(r[u]) else r <-R hashoutput; return(r) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, r': hashoutput) := find[unique] u <= N suchthat defined(x1[u], x2[u], r[u]) && x1' = x1[u] && x2' = x2[u] then return(r' = r[u]) else return(false) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, z1: hashinput1, z2: hashinput2) := return(y1 = z1 && y2 = z2)). param qH [noninteractive]. channel ch1, ch2. let hashoracle(k: key) = foreach iH <= qH do in(ch1, (x1: hashinput1, x2: hashinput2)); out(ch2, hash(k, x1, x2)). } def ROM_hash_refactored_3(key, hashinput1, hashinput2, hashinput3, hashoutput, hash, hashoracle, r, x1, x2, x3, ch1, ch2, qH) { param Nh, N, Neq, Ncoll. fun hash(key, hashinput1, hashinput2, hashinput3):hashoutput. equiv(rom(hash)) foreach ih <= Nh do k <-R key; (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3) := return(hash(k, x1, x2, x3)) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, r': hashoutput) := return(r' = hash(k, x1', x2', x3')) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, z1: hashinput1, z2: hashinput2, z3: hashinput3) := return(hash(k, y1, y2, y3) = hash(k, z1, z2, z3))) <=(#Oeq * Pcoll1rand(hashoutput) + #Ocoll * Pcoll2rand(hashoutput))=> foreach ih <= Nh do (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], r[u]) && x1 = x1[u] && x2 = x2[u] && x3 = x3[u] then return(r[u]) else r <-R hashoutput; return(r) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, r': hashoutput) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], r[u]) && x1' = x1[u] && x2' = x2[u] && x3' = x3[u] then return(r' = r[u]) else return(false) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, z1: hashinput1, z2: hashinput2, z3: hashinput3) := return(y1 = z1 && y2 = z2 && y3 = z3)). param qH [noninteractive]. channel ch1, ch2. let hashoracle(k: key) = foreach iH <= qH do in(ch1, (x1: hashinput1, x2: hashinput2, x3: hashinput3)); out(ch2, hash(k, x1, x2, x3)). } def ROM_hash_refactored_4(key, hashinput1, hashinput2, hashinput3, hashinput4, hashoutput, hash, hashoracle, r, x1, x2, x3, x4, ch1, ch2, qH) { param Nh, N, Neq, Ncoll. fun hash(key, hashinput1, hashinput2, hashinput3, hashinput4):hashoutput. equiv(rom(hash)) foreach ih <= Nh do k <-R key; (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4) := return(hash(k, x1, x2, x3, x4)) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, r': hashoutput) := return(r' = hash(k, x1', x2', x3', x4')) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4) := return(hash(k, y1, y2, y3, y4) = hash(k, z1, z2, z3, z4))) <=(#Oeq * Pcoll1rand(hashoutput) + #Ocoll * Pcoll2rand(hashoutput))=> foreach ih <= Nh do (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], r[u]) && x1 = x1[u] && x2 = x2[u] && x3 = x3[u] && x4 = x4[u] then return(r[u]) else r <-R hashoutput; return(r) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, r': hashoutput) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], r[u]) && x1' = x1[u] && x2' = x2[u] && x3' = x3[u] && x4' = x4[u] then return(r' = r[u]) else return(false) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4) := return(y1 = z1 && y2 = z2 && y3 = z3 && y4 = z4)). param qH [noninteractive]. channel ch1, ch2. let hashoracle(k: key) = foreach iH <= qH do in(ch1, (x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4)); out(ch2, hash(k, x1, x2, x3, x4)). } def ROM_hash_refactored_5(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashoutput, hash, hashoracle, r, x1, x2, x3, x4, x5, ch1, ch2, qH) { param Nh, N, Neq, Ncoll. fun hash(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5):hashoutput. equiv(rom(hash)) foreach ih <= Nh do k <-R key; (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5) := return(hash(k, x1, x2, x3, x4, x5)) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, x5': hashinput5, r': hashoutput) := return(r' = hash(k, x1', x2', x3', x4', x5')) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, y5: hashinput5, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4, z5: hashinput5) := return(hash(k, y1, y2, y3, y4, y5) = hash(k, z1, z2, z3, z4, z5))) <=(#Oeq * Pcoll1rand(hashoutput) + #Ocoll * Pcoll2rand(hashoutput))=> foreach ih <= Nh do (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], x5[u], r[u]) && x1 = x1[u] && x2 = x2[u] && x3 = x3[u] && x4 = x4[u] && x5 = x5[u] then return(r[u]) else r <-R hashoutput; return(r) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, x5': hashinput5, r': hashoutput) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], x5[u], r[u]) && x1' = x1[u] && x2' = x2[u] && x3' = x3[u] && x4' = x4[u] && x5' = x5[u] then return(r' = r[u]) else return(false) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, y5: hashinput5, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4, z5: hashinput5) := return(y1 = z1 && y2 = z2 && y3 = z3 && y4 = z4 && y5 = z5)). param qH [noninteractive]. channel ch1, ch2. let hashoracle(k: key) = foreach iH <= qH do in(ch1, (x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5)); out(ch2, hash(k, x1, x2, x3, x4, x5)). } def ROM_hash_refactored_6(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashoutput, hash, hashoracle, r, x1, x2, x3, x4, x5, x6, ch1, ch2, qH) { param Nh, N, Neq, Ncoll. fun hash(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6):hashoutput. equiv(rom(hash)) foreach ih <= Nh do k <-R key; (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6) := return(hash(k, x1, x2, x3, x4, x5, x6)) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, x5': hashinput5, x6': hashinput6, r': hashoutput) := return(r' = hash(k, x1', x2', x3', x4', x5', x6')) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, y5: hashinput5, y6: hashinput6, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4, z5: hashinput5, z6: hashinput6) := return(hash(k, y1, y2, y3, y4, y5, y6) = hash(k, z1, z2, z3, z4, z5, z6))) <=(#Oeq * Pcoll1rand(hashoutput) + #Ocoll * Pcoll2rand(hashoutput))=> foreach ih <= Nh do (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], x5[u], x6[u], r[u]) && x1 = x1[u] && x2 = x2[u] && x3 = x3[u] && x4 = x4[u] && x5 = x5[u] && x6 = x6[u] then return(r[u]) else r <-R hashoutput; return(r) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, x5': hashinput5, x6': hashinput6, r': hashoutput) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], x5[u], x6[u], r[u]) && x1' = x1[u] && x2' = x2[u] && x3' = x3[u] && x4' = x4[u] && x5' = x5[u] && x6' = x6[u] then return(r' = r[u]) else return(false) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, y5: hashinput5, y6: hashinput6, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4, z5: hashinput5, z6: hashinput6) := return(y1 = z1 && y2 = z2 && y3 = z3 && y4 = z4 && y5 = z5 && y6 = z6)). param qH [noninteractive]. channel ch1, ch2. let hashoracle(k: key) = foreach iH <= qH do in(ch1, (x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6)); out(ch2, hash(k, x1, x2, x3, x4, x5, x6)). } def ROM_hash_refactored_7(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashinput7, hashoutput, hash, hashoracle, r, x1, x2, x3, x4, x5, x6, x7, ch1, ch2, qH) { param Nh, N, Neq, Ncoll. fun hash(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashinput7):hashoutput. equiv(rom(hash)) foreach ih <= Nh do k <-R key; (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6, x7: hashinput7) := return(hash(k, x1, x2, x3, x4, x5, x6, x7)) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, x5': hashinput5, x6': hashinput6, x7': hashinput7, r': hashoutput) := return(r' = hash(k, x1', x2', x3', x4', x5', x6', x7')) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, y5: hashinput5, y6: hashinput6, y7: hashinput7, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4, z5: hashinput5, z6: hashinput6, z7: hashinput7) := return(hash(k, y1, y2, y3, y4, y5, y6, y7) = hash(k, z1, z2, z3, z4, z5, z6, z7))) <=(#Oeq * Pcoll1rand(hashoutput) + #Ocoll * Pcoll2rand(hashoutput))=> foreach ih <= Nh do (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6, x7: hashinput7) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], x5[u], x6[u], x7[u], r[u]) && x1 = x1[u] && x2 = x2[u] && x3 = x3[u] && x4 = x4[u] && x5 = x5[u] && x6 = x6[u] && x7 = x7[u] then return(r[u]) else r <-R hashoutput; return(r) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, x5': hashinput5, x6': hashinput6, x7': hashinput7, r': hashoutput) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], x5[u], x6[u], x7[u], r[u]) && x1' = x1[u] && x2' = x2[u] && x3' = x3[u] && x4' = x4[u] && x5' = x5[u] && x6' = x6[u] && x7' = x7[u] then return(r' = r[u]) else return(false) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, y5: hashinput5, y6: hashinput6, y7: hashinput7, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4, z5: hashinput5, z6: hashinput6, z7: hashinput7) := return(y1 = z1 && y2 = z2 && y3 = z3 && y4 = z4 && y5 = z5 && y6 = z6 && y7 = z7)). param qH [noninteractive]. channel ch1, ch2. let hashoracle(k: key) = foreach iH <= qH do in(ch1, (x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6, x7: hashinput7)); out(ch2, hash(k, x1, x2, x3, x4, x5, x6, x7)). } def ROM_hash_refactored_8(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashinput7, hashinput8, hashoutput, hash, hashoracle, r, x1, x2, x3, x4, x5, x6, x7, x8, ch1, ch2, qH) { param Nh, N, Neq, Ncoll. fun hash(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashinput7, hashinput8):hashoutput. equiv(rom(hash)) foreach ih <= Nh do k <-R key; (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6, x7: hashinput7, x8: hashinput8) := return(hash(k, x1, x2, x3, x4, x5, x6, x7, x8)) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, x5': hashinput5, x6': hashinput6, x7': hashinput7, x8': hashinput8, r': hashoutput) := return(r' = hash(k, x1', x2', x3', x4', x5', x6', x7', x8')) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, y5: hashinput5, y6: hashinput6, y7: hashinput7, y8: hashinput8, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4, z5: hashinput5, z6: hashinput6, z7: hashinput7, z8: hashinput8) := return(hash(k, y1, y2, y3, y4, y5, y6, y7, y8) = hash(k, z1, z2, z3, z4, z5, z6, z7, z8))) <=(#Oeq * Pcoll1rand(hashoutput) + #Ocoll * Pcoll2rand(hashoutput))=> foreach ih <= Nh do (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6, x7: hashinput7, x8: hashinput8) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], x5[u], x6[u], x7[u], x8[u], r[u]) && x1 = x1[u] && x2 = x2[u] && x3 = x3[u] && x4 = x4[u] && x5 = x5[u] && x6 = x6[u] && x7 = x7[u] && x8 = x8[u] then return(r[u]) else r <-R hashoutput; return(r) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, x5': hashinput5, x6': hashinput6, x7': hashinput7, x8': hashinput8, r': hashoutput) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], x5[u], x6[u], x7[u], x8[u], r[u]) && x1' = x1[u] && x2' = x2[u] && x3' = x3[u] && x4' = x4[u] && x5' = x5[u] && x6' = x6[u] && x7' = x7[u] && x8' = x8[u] then return(r' = r[u]) else return(false) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, y5: hashinput5, y6: hashinput6, y7: hashinput7, y8: hashinput8, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4, z5: hashinput5, z6: hashinput6, z7: hashinput7, z8: hashinput8) := return(y1 = z1 && y2 = z2 && y3 = z3 && y4 = z4 && y5 = z5 && y6 = z6 && y7 = z7 && y8 = z8)). param qH [noninteractive]. channel ch1, ch2. let hashoracle(k: key) = foreach iH <= qH do in(ch1, (x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6, x7: hashinput7, x8: hashinput8)); out(ch2, hash(k, x1, x2, x3, x4, x5, x6, x7, x8)). } def ROM_hash_refactored_9(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashinput7, hashinput8, hashinput9, hashoutput, hash, hashoracle, r, x1, x2, x3, x4, x5, x6, x7, x8, x9, ch1, ch2, qH) { param Nh, N, Neq, Ncoll. fun hash(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashinput7, hashinput8, hashinput9):hashoutput. equiv(rom(hash)) foreach ih <= Nh do k <-R key; (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6, x7: hashinput7, x8: hashinput8, x9: hashinput9) := return(hash(k, x1, x2, x3, x4, x5, x6, x7, x8, x9)) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, x5': hashinput5, x6': hashinput6, x7': hashinput7, x8': hashinput8, x9': hashinput9, r': hashoutput) := return(r' = hash(k, x1', x2', x3', x4', x5', x6', x7', x8', x9')) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, y5: hashinput5, y6: hashinput6, y7: hashinput7, y8: hashinput8, y9: hashinput9, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4, z5: hashinput5, z6: hashinput6, z7: hashinput7, z8: hashinput8, z9: hashinput9) := return(hash(k, y1, y2, y3, y4, y5, y6, y7, y8, y9) = hash(k, z1, z2, z3, z4, z5, z6, z7, z8, z9))) <=(#Oeq * Pcoll1rand(hashoutput) + #Ocoll * Pcoll2rand(hashoutput))=> foreach ih <= Nh do (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6, x7: hashinput7, x8: hashinput8, x9: hashinput9) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], x5[u], x6[u], x7[u], x8[u], x9[u], r[u]) && x1 = x1[u] && x2 = x2[u] && x3 = x3[u] && x4 = x4[u] && x5 = x5[u] && x6 = x6[u] && x7 = x7[u] && x8 = x8[u] && x9 = x9[u] then return(r[u]) else r <-R hashoutput; return(r) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, x5': hashinput5, x6': hashinput6, x7': hashinput7, x8': hashinput8, x9': hashinput9, r': hashoutput) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], x5[u], x6[u], x7[u], x8[u], x9[u], r[u]) && x1' = x1[u] && x2' = x2[u] && x3' = x3[u] && x4' = x4[u] && x5' = x5[u] && x6' = x6[u] && x7' = x7[u] && x8' = x8[u] && x9' = x9[u] then return(r' = r[u]) else return(false) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, y5: hashinput5, y6: hashinput6, y7: hashinput7, y8: hashinput8, y9: hashinput9, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4, z5: hashinput5, z6: hashinput6, z7: hashinput7, z8: hashinput8, z9: hashinput9) := return(y1 = z1 && y2 = z2 && y3 = z3 && y4 = z4 && y5 = z5 && y6 = z6 && y7 = z7 && y8 = z8 && y9 = z9)). param qH [noninteractive]. channel ch1, ch2. let hashoracle(k: key) = foreach iH <= qH do in(ch1, (x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6, x7: hashinput7, x8: hashinput8, x9: hashinput9)); out(ch2, hash(k, x1, x2, x3, x4, x5, x6, x7, x8, x9)). } def ROM_hash_refactored_10(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashinput7, hashinput8, hashinput9, hashinput10, hashoutput, hash, hashoracle, r, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, ch1, ch2, qH) { param Nh, N, Neq, Ncoll. fun hash(key, hashinput1, hashinput2, hashinput3, hashinput4, hashinput5, hashinput6, hashinput7, hashinput8, hashinput9, hashinput10):hashoutput. equiv(rom(hash)) foreach ih <= Nh do k <-R key; (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6, x7: hashinput7, x8: hashinput8, x9: hashinput9, x10: hashinput10) := return(hash(k, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, x5': hashinput5, x6': hashinput6, x7': hashinput7, x8': hashinput8, x9': hashinput9, x10': hashinput10, r': hashoutput) := return(r' = hash(k, x1', x2', x3', x4', x5', x6', x7', x8', x9', x10')) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, y5: hashinput5, y6: hashinput6, y7: hashinput7, y8: hashinput8, y9: hashinput9, y10: hashinput10, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4, z5: hashinput5, z6: hashinput6, z7: hashinput7, z8: hashinput8, z9: hashinput9, z10: hashinput10) := return(hash(k, y1, y2, y3, y4, y5, y6, y7, y8, y9, y10) = hash(k, z1, z2, z3, z4, z5, z6, z7, z8, z9, z10))) <=(#Oeq * Pcoll1rand(hashoutput) + #Ocoll * Pcoll2rand(hashoutput))=> foreach ih <= Nh do (foreach i <= N do OH(x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6, x7: hashinput7, x8: hashinput8, x9: hashinput9, x10: hashinput10) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], x5[u], x6[u], x7[u], x8[u], x9[u], x10[u], r[u]) && x1 = x1[u] && x2 = x2[u] && x3 = x3[u] && x4 = x4[u] && x5 = x5[u] && x6 = x6[u] && x7 = x7[u] && x8 = x8[u] && x9 = x9[u] && x10 = x10[u] then return(r[u]) else r <-R hashoutput; return(r) | foreach ieq <= Neq do Oeq(x1': hashinput1, x2': hashinput2, x3': hashinput3, x4': hashinput4, x5': hashinput5, x6': hashinput6, x7': hashinput7, x8': hashinput8, x9': hashinput9, x10': hashinput10, r': hashoutput) := find[unique] u <= N suchthat defined(x1[u], x2[u], x3[u], x4[u], x5[u], x6[u], x7[u], x8[u], x9[u], x10[u], r[u]) && x1' = x1[u] && x2' = x2[u] && x3' = x3[u] && x4' = x4[u] && x5' = x5[u] && x6' = x6[u] && x7' = x7[u] && x8' = x8[u] && x9' = x9[u] && x10' = x10[u] then return(r' = r[u]) else return(false) | foreach icoll <= Ncoll do Ocoll(y1: hashinput1, y2: hashinput2, y3: hashinput3, y4: hashinput4, y5: hashinput5, y6: hashinput6, y7: hashinput7, y8: hashinput8, y9: hashinput9, y10: hashinput10, z1: hashinput1, z2: hashinput2, z3: hashinput3, z4: hashinput4, z5: hashinput5, z6: hashinput6, z7: hashinput7, z8: hashinput8, z9: hashinput9, z10: hashinput10) := return(y1 = z1 && y2 = z2 && y3 = z3 && y4 = z4 && y5 = z5 && y6 = z6 && y7 = z7 && y8 = z8 && y9 = z9 && y10 = z10)). param qH [noninteractive]. channel ch1, ch2. let hashoracle(k: key) = foreach iH <= qH do in(ch1, (x1: hashinput1, x2: hashinput2, x3: hashinput3, x4: hashinput4, x5: hashinput5, x6: hashinput6, x7: hashinput7, x8: hashinput8, x9: hashinput9, x10: hashinput10)); out(ch2, hash(k, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)). } def ROM_hash_refactored(key, hashinput, hashoutput, hash, hashoracle, r, x1, ch1, ch2, qH) { expand ROM_hash_refactored_1(key, hashinput, hashoutput, hash, hashoracle, r, x1, ch1, ch2, qH). } (* split_N defines functions to split a value into N values. input_t: type of the input value part%_t: types of the output parts concat(part1_t, ..., partN_t): input_t: function that takes N parts as input and returns the corresponding value. part%: variable that contains a part input_t and part%_t must be defined before. concat is defined by this macro. *) def split_1(input_t, part1_t, concat, part1) { fun concat(part1_t): input_t [data]. param N. equiv(splitter(concat)) foreach i <= N do r <-R input_t; Or() := return(r) <=(0)=> foreach i <= N do part1 <-R part1_t; Or() := return(concat(part1)). } def split_2(input_t, part1_t, part2_t, concat, part1, part2) { fun concat(part1_t, part2_t): input_t [data]. param N. equiv(splitter(concat)) foreach i <= N do r <-R input_t; Or() := return(r) <=(0)=> foreach i <= N do part1 <-R part1_t; part2 <-R part2_t; Or() := return(concat(part1, part2)). } def split_3(input_t, part1_t, part2_t, part3_t, concat, part1, part2, part3) { fun concat(part1_t, part2_t, part3_t): input_t [data]. param N. equiv(splitter(concat)) foreach i <= N do r <-R input_t; Or() := return(r) <=(0)=> foreach i <= N do part1 <-R part1_t; part2 <-R part2_t; part3 <-R part3_t; Or() := return(concat(part1, part2, part3)). } def split_4(input_t, part1_t, part2_t, part3_t, part4_t, concat, part1, part2, part3, part4) { fun concat(part1_t, part2_t, part3_t, part4_t): input_t [data]. param N. equiv(splitter(concat)) foreach i <= N do r <-R input_t; Or() := return(r) <=(0)=> foreach i <= N do part1 <-R part1_t; part2 <-R part2_t; part3 <-R part3_t; part4 <-R part4_t; Or() := return(concat(part1, part2, part3, part4)). } def split_5(input_t, part1_t, part2_t, part3_t, part4_t, part5_t, concat, part1, part2, part3, part4, part5) { fun concat(part1_t, part2_t, part3_t, part4_t, part5_t): input_t [data]. param N. equiv(splitter(concat)) foreach i <= N do r <-R input_t; Or() := return(r) <=(0)=> foreach i <= N do part1 <-R part1_t; part2 <-R part2_t; part3 <-R part3_t; part4 <-R part4_t; part5 <-R part5_t; Or() := return(concat(part1, part2, part3, part4, part5)). } def split_6(input_t, part1_t, part2_t, part3_t, part4_t, part5_t, part6_t, concat, part1, part2, part3, part4, part5, part6) { fun concat(part1_t, part2_t, part3_t, part4_t, part5_t, part6_t): input_t [data]. param N. equiv(splitter(concat)) foreach i <= N do r <-R input_t; Or() := return(r) <=(0)=> foreach i <= N do part1 <-R part1_t; part2 <-R part2_t; part3 <-R part3_t; part4 <-R part4_t; part5 <-R part5_t; part6 <-R part6_t; Or() := return(concat(part1, part2, part3, part4, part5, part6)). } def split_7(input_t, part1_t, part2_t, part3_t, part4_t, part5_t, part6_t, part7_t, concat, part1, part2, part3, part4, part5, part6, part7) { fun concat(part1_t, part2_t, part3_t, part4_t, part5_t, part6_t, part7_t): input_t [data]. param N. equiv(splitter(concat)) foreach i <= N do r <-R input_t; Or() := return(r) <=(0)=> foreach i <= N do 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; Or() := return(concat(part1, part2, part3, part4, part5, part6, part7)). } def split_8(input_t, part1_t, part2_t, part3_t, part4_t, part5_t, part6_t, part7_t, part8_t, concat, part1, part2, part3, part4, part5, part6, part7, part8) { fun concat(part1_t, part2_t, part3_t, part4_t, part5_t, part6_t, part7_t, part8_t): input_t [data]. param N. equiv(splitter(concat)) foreach i <= N do r <-R input_t; Or() := return(r) <=(0)=> foreach i <= N do 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; Or() := return(concat(part1, part2, part3, part4, part5, part6, part7, part8)). } def split_9(input_t, part1_t, part2_t, part3_t, part4_t, part5_t, part6_t, part7_t, part8_t, part9_t, concat, part1, part2, part3, part4, part5, part6, part7, part8, part9) { fun concat(part1_t, part2_t, part3_t, part4_t, part5_t, part6_t, part7_t, part8_t, part9_t): input_t [data]. param N. equiv(splitter(concat)) foreach i <= N do r <-R input_t; Or() := return(r) <=(0)=> foreach i <= N do 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; Or() := return(concat(part1, part2, part3, part4, part5, part6, part7, part8, part9)). } def split_10(input_t, part1_t, part2_t, part3_t, part4_t, part5_t, part6_t, part7_t, part8_t, part9_t, part10_t, concat, part1, part2, part3, part4, part5, part6, part7, part8, part9, part10) { fun concat(part1_t, part2_t, part3_t, part4_t, part5_t, part6_t, part7_t, part8_t, part9_t, part10_t): input_t [data]. param N. equiv(splitter(concat)) foreach i <= N do r <-R input_t; Or() := return(r) <=(0)=> foreach i <= N do 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; Or() := return(concat(part1, part2, part3, part4, part5, part6, part7, part8, part9, part10)). } (*********************************************************************** The following is part of boolean_choice.cvl inspired by some CryptoVerif examples and Bruno Blanchet ***********************************************************************) def boolean_choice(value_t, test) { fun test(bool, value_t, value_t) : value_t. equation forall x:value_t, y:value_t; test(true, x, y) = x. equation forall x:value_t, y:value_t; test(false, x, y) = y. (* Knowing the equations defined above, this can be deduced, but CryptoVerif can't do this on its own. *) equation forall x:value_t, b:bool; test(b,x,x) = x. } (* Zero needs to be defined already, typically by the AEAD scheme that's * expanded somewhere before. *) def boolean_choice_for_encryption(value_t, Zero, test) { expand boolean_choice(value_t, test). (* Knowing the equations defined above, this can be deduced, but CryptoVerif can't do this on its own. *) equation forall x:value_t, y:value_t, b:bool; Zero(test(b,x,y)) = test (b,Zero(x),Zero(y)). }