Theory Pedersen
subsection‹Pedersen Commitment Scheme›
text‹The Pedersen commitment scheme \<^cite>‹"DBLP:conf/crypto/Pedersen91"› is a commitment scheme based on a cyclic group. We use the
construction of cyclic groups from CryptHOL to formalise the commitment scheme. We prove perfect hiding
and computational binding, with a reduction to the discrete log problem. We a proof of the Pedersen commitment scheme
is realised in the instantiation of the Schnorr ‹Σ›-protocol with the general construction of commitment schemes
from ‹Σ›-protocols. The commitment scheme that is realised there however take the inverse of the message in the commitment
phase due to the construction of the simulator in the ‹Σ›-protocol proof. The two schemes are in some way equal however
as we do not have a well defined notion of equality for commitment schemes we keep this section of the formalisation. This
also serves as reference to the formal proof of the Pedersen commitment scheme we provide in \<^cite>‹"DBLP:conf/post/ButlerAG19"›.›
theory Pedersen imports
Commitment_Schemes
"HOL-Number_Theory.Cong"
Cyclic_Group_Ext
Discrete_Log
Number_Theory_Aux
Uniform_Sampling
begin
locale pedersen_base =
fixes 𝒢 :: "'grp cyclic_group" (structure)
assumes prime_order: "prime (order 𝒢)"
begin
lemma order_gt_0 [simp]: "order 𝒢 > 0"
by (simp add: prime_gt_0_nat prime_order)
type_synonym 'grp' ck = "'grp'"
type_synonym 'grp' vk = "'grp'"
type_synonym plain = "nat"
type_synonym 'grp' commit = "'grp'"
type_synonym "opening" = "nat"
definition key_gen :: "('grp ck × 'grp vk) spmf"
where
"key_gen = do {
x :: nat ← sample_uniform (order 𝒢);
let h = ❙g [^] x;
return_spmf (h, h)
}"
definition commit :: "'grp ck ⇒ plain ⇒ ('grp commit × opening) spmf"
where
"commit ck m = do {
d :: nat ← sample_uniform (order 𝒢);
let c = (❙g [^] d) ⊗ (ck [^] m);
return_spmf (c,d)
}"
definition commit_inv :: "'grp ck ⇒ plain ⇒ ('grp commit × opening) spmf"
where
"commit_inv ck m = do {
d :: nat ← sample_uniform (order 𝒢);
let c = (❙g [^] d) ⊗ (inv ck [^] m);
return_spmf (c,d)
}"
definition verify :: "'grp vk ⇒ plain ⇒ 'grp commit ⇒ opening ⇒ bool"
where
"verify v_key m c d = (c = (❙g [^] d ⊗ v_key [^] m))"
definition valid_msg :: "plain ⇒ bool"
where "valid_msg msg ≡ (msg < order 𝒢)"
definition dis_log_𝒜 :: "('grp ck, plain, 'grp commit, opening) bind_adversary ⇒ 'grp ck ⇒ nat spmf"
where "dis_log_𝒜 𝒜 h = do {
(c, m, d, m', d') ← 𝒜 h;
_ :: unit ← assert_spmf (m ≠ m' ∧ valid_msg m ∧ valid_msg m');
_ :: unit ← assert_spmf (c = ❙g [^] d ⊗ h [^] m ∧ c = ❙g [^] d' ⊗ h [^] m');
return_spmf (if (m > m') then (nat ((int d' - int d) * inverse (m - m') (order 𝒢) mod order 𝒢)) else
(nat ((int d - int d') * inverse (m' - m) (order 𝒢) mod order 𝒢)))}"
sublocale ped_commit: abstract_commitment key_gen commit verify valid_msg .
sublocale discrete_log: dis_log _
unfolding dis_log_def by(simp)
end
locale pedersen = pedersen_base + cyclic_group 𝒢
begin
lemma mod_one_cancel: assumes "[int y * z * x = y' * x] (mod order 𝒢)" and "[z * x = 1] (mod order 𝒢)"
shows "[int y = y' * x] (mod order 𝒢)"
by (metis assms Groups.mult_ac(2) cong_scalar_right cong_sym_eq cong_trans more_arith_simps(11) more_arith_simps(5))
lemma dis_log_break:
fixes d d' m m' :: nat
assumes c: " ❙g [^] d' ⊗ (❙g [^] y) [^] m' = ❙g [^] d ⊗ (❙g [^] y) [^] m"
and y_less_order: "y < order 𝒢"
and m_ge_m': "m > m'"
and m: "m < order 𝒢"
shows "y = nat ((int d' - int d) * (fst (bezw ((m - m')) (order 𝒢))) mod order 𝒢)"
proof -
have mm': "¬ [m = m'] (mod order 𝒢)"
using m m_ge_m' basic_trans_rules(19) cong_less_modulus_unique_nat by blast
hence gcd: "int (gcd ((m - m')) (order 𝒢)) = 1"
using assms(3) assms(4) prime_field prime_order by auto
have "❙g [^] (d + y * m) = ❙g [^] (d' + y * m')"
using c by (simp add: nat_pow_mult nat_pow_pow)
hence "[d + y * m = d' + y * m'] (mod order 𝒢)"
by(simp add: pow_generator_eq_iff_cong finite_carrier)
hence "[int d + int y * int m = int d' + int y * int m'] (mod order 𝒢)"
using cong_int_iff by force
from cong_diff[OF this cong_refl, of "int d + int y * int m'"]
have "[int y * int (m - m') = int d' - int d] (mod order 𝒢)" using m_ge_m'
by(simp add: int_distrib of_nat_diff)
hence *: "[int y * int (m - m') * (fst (bezw ((m - m')) (order 𝒢))) = (int d' - int d) * (fst (bezw ((m - m')) (order 𝒢)))] (mod order 𝒢)"
by (simp add: cong_scalar_right)
hence "[int y * (int (m - m') * (fst (bezw ((m - m')) (order 𝒢)))) = (int d' - int d) * (fst (bezw ((m - m')) (order 𝒢)))] (mod order 𝒢)"
by (simp add: more_arith_simps(11))
hence "[int y * 1 = (int d' - int d) * (fst (bezw ((m - m')) (order 𝒢)))] (mod order 𝒢)"
using inverse gcd
by (smt Groups.mult_ac(2) Number_Theory_Aux.inverse Totient.of_nat_eq_1_iff * cong_def int_ops(9) mod_mult_right_eq mod_one_cancel)
hence "[int y = (int d' - int d) * (fst (bezw ((m - m')) (order 𝒢)))] (mod order 𝒢)" by simp
hence "y mod order 𝒢 = (int d' - int d) * (fst (bezw ((m - m')) (order 𝒢))) mod order 𝒢"
using cong_def zmod_int by auto
thus ?thesis using y_less_order by simp
qed
lemma dis_log_break':
assumes "y < order 𝒢"
and "¬ m' < m"
and "m ≠ m'"
and m: "m' < order 𝒢"
and "❙g [^] d ⊗ (❙g [^] y) [^] m = ❙g [^] d' ⊗ (❙g [^] y) [^] m'"
shows "y = nat ((int d - int d') * fst (bezw ((m' - m)) (order 𝒢)) mod int (order 𝒢))"
proof-
have "m' > m" using assms
using group_eq_pow_eq_mod nat_neq_iff order_gt_0 by blast
thus ?thesis
using dis_log_break[of d y m d' m' ]assms cong_sym_eq assms by blast
qed
lemma set_spmf_samp_uni [simp]: "set_spmf (sample_uniform (order 𝒢)) = {x. x < order 𝒢}"
by(auto simp add: sample_uniform_def)
lemma correct:
shows "spmf (ped_commit.correct_game m) True = 1"
using finite_carrier order_gt_0_iff_finite
apply(simp add: abstract_commitment.correct_game_def Let_def commit_def verify_def)
by(simp add: key_gen_def Let_def bind_spmf_const cong: bind_spmf_cong_simp)
theorem abstract_correct:
shows "ped_commit.correct"
unfolding abstract_commitment.correct_def using correct by simp
lemma perfect_hiding:
shows "spmf (ped_commit.hiding_game_ind_cpa 𝒜) True - 1/2 = 0"
including monad_normalisation
proof -
obtain 𝒜1 𝒜2 where [simp]: "𝒜 = (𝒜1, 𝒜2)" by(cases 𝒜)
note [simp] = Let_def split_def
have "ped_commit.hiding_game_ind_cpa (𝒜1, 𝒜2) = TRY do {
(ck,vk) ← key_gen;
((m0, m1), σ) ← 𝒜1 vk;
_ :: unit ← assert_spmf (valid_msg m0 ∧ valid_msg m1);
b ← coin_spmf;
(c,d) ← commit ck (if b then m0 else m1);
b' ← 𝒜2 c σ;
return_spmf (b' = b)} ELSE coin_spmf"
by(simp add: abstract_commitment.hiding_game_ind_cpa_def)
also have "... = TRY do {
x :: nat ← sample_uniform (order 𝒢);
let h = ❙g [^] x;
((m0, m1), σ) ← 𝒜1 h;
_ :: unit ← assert_spmf (valid_msg m0 ∧ valid_msg m1);
b ← coin_spmf;
d :: nat ← sample_uniform (order 𝒢);
let c = ((❙g [^] d) ⊗ (h [^] (if b then m0 else m1)));
b' ← 𝒜2 c σ;
return_spmf (b' = b)} ELSE coin_spmf"
by(simp add: commit_def key_gen_def)
also have "... = TRY do {
x :: nat ← sample_uniform (order 𝒢);
let h = (❙g [^] x);
((m0, m1), σ) ← 𝒜1 h;
_ :: unit ← assert_spmf (valid_msg m0 ∧ valid_msg m1);
b ← coin_spmf;
z ← map_spmf (λz. ❙g [^] z ⊗ (h [^] (if b then m0 else m1))) (sample_uniform (order 𝒢));
guess :: bool ← 𝒜2 z σ;
return_spmf(guess = b)} ELSE coin_spmf"
by(simp add: bind_map_spmf o_def)
also have "... = TRY do {
x :: nat ← sample_uniform (order 𝒢);
let h = (❙g [^] x);
((m0, m1), σ) ← 𝒜1 h;
_ :: unit ← assert_spmf (valid_msg m0 ∧ valid_msg m1);
b ← coin_spmf;
z ← map_spmf (λz. ❙g [^] z) (sample_uniform (order 𝒢));
guess :: bool ← 𝒜2 z σ;
return_spmf(guess = b)} ELSE coin_spmf"
by(simp add: sample_uniform_one_time_pad)
also have "... = TRY do {
x :: nat ← sample_uniform (order 𝒢);
let h = (❙g [^] x);
((m0, m1), σ) ← 𝒜1 h;
_ :: unit ← assert_spmf (valid_msg m0 ∧ valid_msg m1);
z ← map_spmf (λz. ❙g [^] z) (sample_uniform (order 𝒢));
guess :: bool ← 𝒜2 z σ;
map_spmf((=) guess) coin_spmf} ELSE coin_spmf"
by(simp add: map_spmf_conv_bind_spmf)
also have "... = coin_spmf"
by(auto simp add: bind_spmf_const map_eq_const_coin_spmf try_bind_spmf_lossless2' scale_bind_spmf weight_spmf_le_1 scale_scale_spmf)
ultimately show ?thesis by(simp add: spmf_of_set)
qed
theorem abstract_perfect_hiding:
shows "ped_commit.perfect_hiding_ind_cpa 𝒜"
proof-
have "spmf (ped_commit.hiding_game_ind_cpa 𝒜) True - 1/2 = 0"
using perfect_hiding by fastforce
thus ?thesis
by(simp add: abstract_commitment.perfect_hiding_ind_cpa_def abstract_commitment.hiding_advantage_ind_cpa_def)
qed
lemma bind_output_cong:
assumes "x < order 𝒢"
shows "(x = nat ((int b - int ab) * fst (bezw (aa - ac) (order 𝒢)) mod int (order 𝒢)))
⟷ [x = nat ((int b - int ab) * fst (bezw (aa - ac) (order 𝒢)) mod int (order 𝒢))] (mod order 𝒢)"
using assms cong_less_modulus_unique_nat nat_less_iff by auto
lemma bind_game_eq_dis_log:
shows "ped_commit.bind_game 𝒜 = discrete_log.dis_log (dis_log_𝒜 𝒜)"
proof-
note [simp] = Let_def split_def
have "ped_commit.bind_game 𝒜 = TRY do {
(ck,vk) ← key_gen;
(c, m, d, m', d') ← 𝒜 ck;
_ :: unit ← assert_spmf(m ≠ m' ∧ valid_msg m ∧ valid_msg m');
let b = verify vk m c d;
let b' = verify vk m' c d';
_ :: unit ← assert_spmf (b ∧ b');
return_spmf True} ELSE return_spmf False"
by(simp add: abstract_commitment.bind_game_alt_def)
also have "... = TRY do {
x :: nat ← sample_uniform (Coset.order 𝒢);
(c, m, d, m', d') ← 𝒜 (❙g [^] x);
_ :: unit ← assert_spmf (m ≠ m' ∧ valid_msg m ∧ valid_msg m');
_ :: unit ← assert_spmf (c = ❙g [^] d ⊗ (❙g [^] x) [^] m ∧ c = ❙g [^] d' ⊗ (❙g [^] x) [^] m');
return_spmf True} ELSE return_spmf False"
by(simp add: verify_def key_gen_def)
also have "... = TRY do {
x :: nat ← sample_uniform (order 𝒢);
(c, m, d, m', d') ← 𝒜 (❙g [^] x);
_ :: unit ← assert_spmf (m ≠ m' ∧ valid_msg m ∧ valid_msg m');
_ :: unit ← assert_spmf (c = ❙g [^] d ⊗ (❙g [^] x) [^] m ∧ c = ❙g [^] d' ⊗ (❙g [^] x) [^] m');
return_spmf (x = (if (m > m') then (nat ((int d' - int d) * (fst (bezw ((m - m')) (order 𝒢))) mod order 𝒢)) else
(nat ((int d - int d') * (fst (bezw ((m' - m)) (order 𝒢))) mod order 𝒢))))} ELSE return_spmf False"
apply(intro try_spmf_cong bind_spmf_cong[OF refl]; clarsimp?)
apply(auto simp add: valid_msg_def)
apply(intro bind_spmf_cong[OF refl]; clarsimp?)+
apply(simp add: dis_log_break)
apply(intro bind_spmf_cong[OF refl]; clarsimp?)+
by(simp add: dis_log_break')
ultimately show ?thesis
apply(simp add: discrete_log.dis_log_def dis_log_𝒜_def cong: bind_spmf_cong_simp)
apply(intro try_spmf_cong bind_spmf_cong[OF refl]; clarsimp?)+
using bind_output_cong by auto
qed
theorem pedersen_bind: "ped_commit.bind_advantage 𝒜 = discrete_log.advantage (dis_log_𝒜 𝒜)"
unfolding abstract_commitment.bind_advantage_def discrete_log.advantage_def
using bind_game_eq_dis_log by simp
end
locale pedersen_asymp =
fixes 𝒢 :: "nat ⇒ 'grp cyclic_group"
assumes pedersen: "⋀η. pedersen (𝒢 η)"
begin
sublocale pedersen "𝒢 η" for η by(simp add: pedersen)
theorem pedersen_correct_asym:
shows "ped_commit.correct n"
using abstract_correct by simp
theorem pedersen_perfect_hiding_asym:
shows "ped_commit.perfect_hiding_ind_cpa n (𝒜 n)"
by (simp add: abstract_perfect_hiding)
theorem pedersen_bind_asym:
shows "negligible (λ n. ped_commit.bind_advantage n (𝒜 n))
⟷ negligible (λ n. discrete_log.advantage n (dis_log_𝒜 n (𝒜 n)))"
by(simp add: pedersen_bind)
end
end