charm-crypto-framework 0.61.1__cp313-cp313-macosx_10_13_universal2.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (323) hide show
  1. charm/__init__.py +5 -0
  2. charm/adapters/__init__.py +0 -0
  3. charm/adapters/abenc_adapt_hybrid.py +90 -0
  4. charm/adapters/dabenc_adapt_hybrid.py +145 -0
  5. charm/adapters/ibenc_adapt_hybrid.py +72 -0
  6. charm/adapters/ibenc_adapt_identityhash.py +80 -0
  7. charm/adapters/kpabenc_adapt_hybrid.py +91 -0
  8. charm/adapters/pkenc_adapt_bchk05.py +121 -0
  9. charm/adapters/pkenc_adapt_chk04.py +91 -0
  10. charm/adapters/pkenc_adapt_hybrid.py +98 -0
  11. charm/adapters/pksig_adapt_naor01.py +89 -0
  12. charm/config.py +7 -0
  13. charm/core/__init__.py +0 -0
  14. charm/core/benchmark/benchmark_util.c +353 -0
  15. charm/core/benchmark/benchmark_util.h +61 -0
  16. charm/core/benchmark/benchmarkmodule.c +476 -0
  17. charm/core/benchmark/benchmarkmodule.h +162 -0
  18. charm/core/benchmark.cpython-313-darwin.so +0 -0
  19. charm/core/crypto/AES/AES.c +1464 -0
  20. charm/core/crypto/AES.cpython-313-darwin.so +0 -0
  21. charm/core/crypto/DES/DES.c +113 -0
  22. charm/core/crypto/DES.cpython-313-darwin.so +0 -0
  23. charm/core/crypto/DES3/DES3.c +26 -0
  24. charm/core/crypto/DES3.cpython-313-darwin.so +0 -0
  25. charm/core/crypto/__init__.py +0 -0
  26. charm/core/crypto/cryptobase/XOR.c +80 -0
  27. charm/core/crypto/cryptobase/_counter.c +496 -0
  28. charm/core/crypto/cryptobase/_counter.h +54 -0
  29. charm/core/crypto/cryptobase/block_template.c +900 -0
  30. charm/core/crypto/cryptobase/block_template.h +69 -0
  31. charm/core/crypto/cryptobase/cryptobasemodule.c +220 -0
  32. charm/core/crypto/cryptobase/libtom/tomcrypt.h +90 -0
  33. charm/core/crypto/cryptobase/libtom/tomcrypt_argchk.h +44 -0
  34. charm/core/crypto/cryptobase/libtom/tomcrypt_cfg.h +186 -0
  35. charm/core/crypto/cryptobase/libtom/tomcrypt_cipher.h +941 -0
  36. charm/core/crypto/cryptobase/libtom/tomcrypt_custom.h +556 -0
  37. charm/core/crypto/cryptobase/libtom/tomcrypt_des.c +1912 -0
  38. charm/core/crypto/cryptobase/libtom/tomcrypt_hash.h +407 -0
  39. charm/core/crypto/cryptobase/libtom/tomcrypt_mac.h +496 -0
  40. charm/core/crypto/cryptobase/libtom/tomcrypt_macros.h +435 -0
  41. charm/core/crypto/cryptobase/libtom/tomcrypt_math.h +534 -0
  42. charm/core/crypto/cryptobase/libtom/tomcrypt_misc.h +103 -0
  43. charm/core/crypto/cryptobase/libtom/tomcrypt_pk.h +653 -0
  44. charm/core/crypto/cryptobase/libtom/tomcrypt_pkcs.h +90 -0
  45. charm/core/crypto/cryptobase/libtom/tomcrypt_prng.h +199 -0
  46. charm/core/crypto/cryptobase/stream_template.c +271 -0
  47. charm/core/crypto/cryptobase/strxor.c +229 -0
  48. charm/core/crypto/cryptobase.cpython-313-darwin.so +0 -0
  49. charm/core/engine/__init__.py +5 -0
  50. charm/core/engine/protocol.py +293 -0
  51. charm/core/engine/util.py +174 -0
  52. charm/core/math/__init__.py +0 -0
  53. charm/core/math/elliptic_curve/ecmodule.c +1986 -0
  54. charm/core/math/elliptic_curve/ecmodule.h +230 -0
  55. charm/core/math/elliptic_curve.cpython-313-darwin.so +0 -0
  56. charm/core/math/elliptic_curve.pyi +63 -0
  57. charm/core/math/integer/integermodule.c +2539 -0
  58. charm/core/math/integer/integermodule.h +145 -0
  59. charm/core/math/integer.cpython-313-darwin.so +0 -0
  60. charm/core/math/integer.pyi +76 -0
  61. charm/core/math/pairing/miracl/miracl_config.h +37 -0
  62. charm/core/math/pairing/miracl/miracl_interface.h +118 -0
  63. charm/core/math/pairing/miracl/miracl_interface2.h +126 -0
  64. charm/core/math/pairing/miracl/pairingmodule2.c +2094 -0
  65. charm/core/math/pairing/miracl/pairingmodule2.h +307 -0
  66. charm/core/math/pairing/pairingmodule.c +2230 -0
  67. charm/core/math/pairing/pairingmodule.h +241 -0
  68. charm/core/math/pairing/relic/pairingmodule3.c +1853 -0
  69. charm/core/math/pairing/relic/pairingmodule3.h +233 -0
  70. charm/core/math/pairing/relic/relic_interface.c +1337 -0
  71. charm/core/math/pairing/relic/relic_interface.h +217 -0
  72. charm/core/math/pairing/relic/test_relic.c +171 -0
  73. charm/core/math/pairing.cpython-313-darwin.so +0 -0
  74. charm/core/math/pairing.pyi +69 -0
  75. charm/core/utilities/base64.c +248 -0
  76. charm/core/utilities/base64.h +15 -0
  77. charm/schemes/__init__.py +0 -0
  78. charm/schemes/abenc/__init__.py +0 -0
  79. charm/schemes/abenc/abenc_accountability_jyjxgd20.py +647 -0
  80. charm/schemes/abenc/abenc_bsw07.py +146 -0
  81. charm/schemes/abenc/abenc_ca_cpabe_ar17.py +684 -0
  82. charm/schemes/abenc/abenc_dacmacs_yj14.py +298 -0
  83. charm/schemes/abenc/abenc_lsw08.py +159 -0
  84. charm/schemes/abenc/abenc_maabe_rw15.py +236 -0
  85. charm/schemes/abenc/abenc_maabe_yj14.py +297 -0
  86. charm/schemes/abenc/abenc_tbpre_lww14.py +309 -0
  87. charm/schemes/abenc/abenc_unmcpabe_yahk14.py +223 -0
  88. charm/schemes/abenc/abenc_waters09.py +144 -0
  89. charm/schemes/abenc/abenc_yct14.py +208 -0
  90. charm/schemes/abenc/abenc_yllc15.py +178 -0
  91. charm/schemes/abenc/ac17.py +248 -0
  92. charm/schemes/abenc/bsw07.py +141 -0
  93. charm/schemes/abenc/cgw15.py +277 -0
  94. charm/schemes/abenc/dabe_aw11.py +204 -0
  95. charm/schemes/abenc/dfa_fe12.py +144 -0
  96. charm/schemes/abenc/pk_hve08.py +179 -0
  97. charm/schemes/abenc/waters11.py +143 -0
  98. charm/schemes/aggrsign_MuSig.py +150 -0
  99. charm/schemes/aggrsign_bls.py +267 -0
  100. charm/schemes/blindsig_ps16.py +654 -0
  101. charm/schemes/chamhash_adm05.py +113 -0
  102. charm/schemes/chamhash_rsa_hw09.py +100 -0
  103. charm/schemes/commit/__init__.py +0 -0
  104. charm/schemes/commit/commit_gs08.py +77 -0
  105. charm/schemes/commit/commit_pedersen92.py +53 -0
  106. charm/schemes/encap_bchk05.py +62 -0
  107. charm/schemes/grpsig/__init__.py +0 -0
  108. charm/schemes/grpsig/groupsig_bgls04.py +114 -0
  109. charm/schemes/grpsig/groupsig_bgls04_var.py +115 -0
  110. charm/schemes/hibenc/__init__.py +0 -0
  111. charm/schemes/hibenc/hibenc_bb04.py +105 -0
  112. charm/schemes/hibenc/hibenc_lew11.py +193 -0
  113. charm/schemes/ibenc/__init__.py +0 -0
  114. charm/schemes/ibenc/clpkc_rp03.py +119 -0
  115. charm/schemes/ibenc/ibenc_CW13_z.py +168 -0
  116. charm/schemes/ibenc/ibenc_bb03.py +94 -0
  117. charm/schemes/ibenc/ibenc_bf01.py +121 -0
  118. charm/schemes/ibenc/ibenc_ckrs09.py +120 -0
  119. charm/schemes/ibenc/ibenc_cllww12_z.py +172 -0
  120. charm/schemes/ibenc/ibenc_lsw08.py +120 -0
  121. charm/schemes/ibenc/ibenc_sw05.py +238 -0
  122. charm/schemes/ibenc/ibenc_waters05.py +144 -0
  123. charm/schemes/ibenc/ibenc_waters05_z.py +164 -0
  124. charm/schemes/ibenc/ibenc_waters09.py +107 -0
  125. charm/schemes/ibenc/ibenc_waters09_z.py +147 -0
  126. charm/schemes/joye_scheme.py +106 -0
  127. charm/schemes/lem_scheme.py +207 -0
  128. charm/schemes/pk_fre_ccv11.py +107 -0
  129. charm/schemes/pk_vrf.py +127 -0
  130. charm/schemes/pkenc/__init__.py +0 -0
  131. charm/schemes/pkenc/pkenc_cs98.py +108 -0
  132. charm/schemes/pkenc/pkenc_elgamal85.py +122 -0
  133. charm/schemes/pkenc/pkenc_gm82.py +98 -0
  134. charm/schemes/pkenc/pkenc_paillier99.py +118 -0
  135. charm/schemes/pkenc/pkenc_rabin.py +254 -0
  136. charm/schemes/pkenc/pkenc_rsa.py +186 -0
  137. charm/schemes/pksig/__init__.py +0 -0
  138. charm/schemes/pksig/pksig_CW13_z.py +135 -0
  139. charm/schemes/pksig/pksig_bls04.py +87 -0
  140. charm/schemes/pksig/pksig_boyen.py +156 -0
  141. charm/schemes/pksig/pksig_chch.py +97 -0
  142. charm/schemes/pksig/pksig_chp.py +70 -0
  143. charm/schemes/pksig/pksig_cl03.py +150 -0
  144. charm/schemes/pksig/pksig_cl04.py +87 -0
  145. charm/schemes/pksig/pksig_cllww12_z.py +142 -0
  146. charm/schemes/pksig/pksig_cyh.py +132 -0
  147. charm/schemes/pksig/pksig_dsa.py +76 -0
  148. charm/schemes/pksig/pksig_ecdsa.py +71 -0
  149. charm/schemes/pksig/pksig_hess.py +104 -0
  150. charm/schemes/pksig/pksig_hw.py +110 -0
  151. charm/schemes/pksig/pksig_lamport.py +63 -0
  152. charm/schemes/pksig/pksig_ps01.py +135 -0
  153. charm/schemes/pksig/pksig_ps02.py +124 -0
  154. charm/schemes/pksig/pksig_ps03.py +119 -0
  155. charm/schemes/pksig/pksig_rsa_hw09.py +206 -0
  156. charm/schemes/pksig/pksig_schnorr91.py +77 -0
  157. charm/schemes/pksig/pksig_waters.py +115 -0
  158. charm/schemes/pksig/pksig_waters05.py +121 -0
  159. charm/schemes/pksig/pksig_waters09.py +121 -0
  160. charm/schemes/pre_mg07.py +150 -0
  161. charm/schemes/prenc/pre_afgh06.py +126 -0
  162. charm/schemes/prenc/pre_bbs98.py +123 -0
  163. charm/schemes/prenc/pre_nal16.py +216 -0
  164. charm/schemes/protocol_a01.py +272 -0
  165. charm/schemes/protocol_ao00.py +215 -0
  166. charm/schemes/protocol_cns07.py +274 -0
  167. charm/schemes/protocol_schnorr91.py +125 -0
  168. charm/schemes/sigma1.py +64 -0
  169. charm/schemes/sigma2.py +129 -0
  170. charm/schemes/sigma3.py +126 -0
  171. charm/schemes/threshold/__init__.py +59 -0
  172. charm/schemes/threshold/dkls23_dkg.py +556 -0
  173. charm/schemes/threshold/dkls23_presign.py +1089 -0
  174. charm/schemes/threshold/dkls23_sign.py +761 -0
  175. charm/schemes/threshold/xrpl_wallet.py +967 -0
  176. charm/test/__init__.py +0 -0
  177. charm/test/adapters/__init__.py +0 -0
  178. charm/test/adapters/abenc_adapt_hybrid_test.py +29 -0
  179. charm/test/adapters/dabenc_adapt_hybrid_test.py +56 -0
  180. charm/test/adapters/ibenc_adapt_hybrid_test.py +36 -0
  181. charm/test/adapters/ibenc_adapt_identityhash_test.py +32 -0
  182. charm/test/adapters/kpabenc_adapt_hybrid_test.py +30 -0
  183. charm/test/benchmark/abenc_yllc15_bench.py +92 -0
  184. charm/test/benchmark/benchmark_test.py +148 -0
  185. charm/test/benchmark_threshold.py +260 -0
  186. charm/test/conftest.py +38 -0
  187. charm/test/fuzz/__init__.py +1 -0
  188. charm/test/fuzz/conftest.py +5 -0
  189. charm/test/fuzz/fuzz_policy_parser.py +76 -0
  190. charm/test/fuzz/fuzz_serialization.py +83 -0
  191. charm/test/schemes/__init__.py +0 -0
  192. charm/test/schemes/abenc/__init__.py +0 -0
  193. charm/test/schemes/abenc/abenc_bsw07_test.py +39 -0
  194. charm/test/schemes/abenc/abenc_dacmacs_yj14_test.py +16 -0
  195. charm/test/schemes/abenc/abenc_lsw08_test.py +33 -0
  196. charm/test/schemes/abenc/abenc_maabe_yj14_test.py +16 -0
  197. charm/test/schemes/abenc/abenc_tbpre_lww14_test.py +16 -0
  198. charm/test/schemes/abenc/abenc_waters09_test.py +38 -0
  199. charm/test/schemes/abenc/abenc_yllc15_test.py +74 -0
  200. charm/test/schemes/chamhash_adm05_test.py +31 -0
  201. charm/test/schemes/chamhash_rsa_hw09_test.py +29 -0
  202. charm/test/schemes/commit/__init__.py +0 -0
  203. charm/test/schemes/commit/commit_gs08_test.py +24 -0
  204. charm/test/schemes/commit/commit_pedersen92_test.py +26 -0
  205. charm/test/schemes/dabe_aw11_test.py +45 -0
  206. charm/test/schemes/encap_bchk05_test.py +21 -0
  207. charm/test/schemes/grpsig/__init__.py +0 -0
  208. charm/test/schemes/grpsig/groupsig_bgls04_test.py +35 -0
  209. charm/test/schemes/grpsig/groupsig_bgls04_var_test.py +39 -0
  210. charm/test/schemes/hibenc/__init__.py +0 -0
  211. charm/test/schemes/hibenc/hibenc_bb04_test.py +28 -0
  212. charm/test/schemes/ibenc/__init__.py +0 -0
  213. charm/test/schemes/ibenc/ibenc_bb03_test.py +26 -0
  214. charm/test/schemes/ibenc/ibenc_bf01_test.py +24 -0
  215. charm/test/schemes/ibenc/ibenc_ckrs09_test.py +25 -0
  216. charm/test/schemes/ibenc/ibenc_lsw08_test.py +31 -0
  217. charm/test/schemes/ibenc/ibenc_sw05_test.py +32 -0
  218. charm/test/schemes/ibenc/ibenc_waters05_test.py +31 -0
  219. charm/test/schemes/ibenc/ibenc_waters09_test.py +27 -0
  220. charm/test/schemes/pk_vrf_test.py +29 -0
  221. charm/test/schemes/pkenc/__init__.py +0 -0
  222. charm/test/schemes/pkenc_test.py +255 -0
  223. charm/test/schemes/pksig/__init__.py +0 -0
  224. charm/test/schemes/pksig_test.py +376 -0
  225. charm/test/schemes/rsa_alg_test.py +340 -0
  226. charm/test/schemes/threshold_test.py +1792 -0
  227. charm/test/serialize/__init__.py +0 -0
  228. charm/test/serialize/serialize_test.py +40 -0
  229. charm/test/toolbox/__init__.py +0 -0
  230. charm/test/toolbox/conversion_test.py +30 -0
  231. charm/test/toolbox/ecgroup_test.py +53 -0
  232. charm/test/toolbox/integer_arithmetic_test.py +441 -0
  233. charm/test/toolbox/paddingschemes_test.py +238 -0
  234. charm/test/toolbox/policy_parser_stress_test.py +969 -0
  235. charm/test/toolbox/secretshare_test.py +28 -0
  236. charm/test/toolbox/symcrypto_test.py +108 -0
  237. charm/test/toolbox/test_policy_expression.py +16 -0
  238. charm/test/vectors/__init__.py +1 -0
  239. charm/test/vectors/test_bls_vectors.py +289 -0
  240. charm/test/vectors/test_pedersen_vectors.py +315 -0
  241. charm/test/vectors/test_schnorr_vectors.py +368 -0
  242. charm/test/zkp_compiler/__init__.py +9 -0
  243. charm/test/zkp_compiler/benchmark_zkp.py +258 -0
  244. charm/test/zkp_compiler/test_and_proof.py +240 -0
  245. charm/test/zkp_compiler/test_batch_verify.py +248 -0
  246. charm/test/zkp_compiler/test_dleq_proof.py +264 -0
  247. charm/test/zkp_compiler/test_or_proof.py +231 -0
  248. charm/test/zkp_compiler/test_proof_serialization.py +121 -0
  249. charm/test/zkp_compiler/test_range_proof.py +241 -0
  250. charm/test/zkp_compiler/test_representation_proof.py +325 -0
  251. charm/test/zkp_compiler/test_schnorr_proof.py +221 -0
  252. charm/test/zkp_compiler/test_thread_safety.py +169 -0
  253. charm/test/zkp_compiler/test_zkp_parser.py +139 -0
  254. charm/toolbox/ABEnc.py +26 -0
  255. charm/toolbox/ABEncMultiAuth.py +66 -0
  256. charm/toolbox/ABEnumeric.py +800 -0
  257. charm/toolbox/Commit.py +24 -0
  258. charm/toolbox/DFA.py +89 -0
  259. charm/toolbox/FSA.py +1254 -0
  260. charm/toolbox/Hash.py +39 -0
  261. charm/toolbox/IBEnc.py +62 -0
  262. charm/toolbox/IBSig.py +64 -0
  263. charm/toolbox/PKEnc.py +66 -0
  264. charm/toolbox/PKSig.py +56 -0
  265. charm/toolbox/PREnc.py +32 -0
  266. charm/toolbox/ZKProof.py +289 -0
  267. charm/toolbox/__init__.py +0 -0
  268. charm/toolbox/bitstring.py +49 -0
  269. charm/toolbox/broadcast.py +220 -0
  270. charm/toolbox/conversion.py +100 -0
  271. charm/toolbox/eccurve.py +149 -0
  272. charm/toolbox/ecgroup.py +143 -0
  273. charm/toolbox/enum.py +60 -0
  274. charm/toolbox/hash_module.py +91 -0
  275. charm/toolbox/integergroup.py +323 -0
  276. charm/toolbox/iterate.py +22 -0
  277. charm/toolbox/matrixops.py +76 -0
  278. charm/toolbox/mpc_utils.py +296 -0
  279. charm/toolbox/msp.py +175 -0
  280. charm/toolbox/mta.py +985 -0
  281. charm/toolbox/node.py +120 -0
  282. charm/toolbox/ot/__init__.py +22 -0
  283. charm/toolbox/ot/base_ot.py +374 -0
  284. charm/toolbox/ot/dpf.py +642 -0
  285. charm/toolbox/ot/mpfss.py +228 -0
  286. charm/toolbox/ot/ot_extension.py +589 -0
  287. charm/toolbox/ot/silent_ot.py +378 -0
  288. charm/toolbox/paddingschemes.py +423 -0
  289. charm/toolbox/paddingschemes_test.py +238 -0
  290. charm/toolbox/pairingcurves.py +85 -0
  291. charm/toolbox/pairinggroup.py +186 -0
  292. charm/toolbox/policy_expression_spec.py +70 -0
  293. charm/toolbox/policytree.py +189 -0
  294. charm/toolbox/reCompiler.py +346 -0
  295. charm/toolbox/redundancyschemes.py +65 -0
  296. charm/toolbox/schemebase.py +188 -0
  297. charm/toolbox/secretshare.py +104 -0
  298. charm/toolbox/secretutil.py +174 -0
  299. charm/toolbox/securerandom.py +73 -0
  300. charm/toolbox/sigmaprotocol.py +46 -0
  301. charm/toolbox/specialprimes.py +45 -0
  302. charm/toolbox/symcrypto.py +279 -0
  303. charm/toolbox/threshold_sharing.py +553 -0
  304. charm/toolbox/xmlserialize.py +94 -0
  305. charm/toolbox/zknode.py +105 -0
  306. charm/zkp_compiler/__init__.py +89 -0
  307. charm/zkp_compiler/and_proof.py +460 -0
  308. charm/zkp_compiler/batch_verify.py +324 -0
  309. charm/zkp_compiler/dleq_proof.py +423 -0
  310. charm/zkp_compiler/or_proof.py +305 -0
  311. charm/zkp_compiler/range_proof.py +417 -0
  312. charm/zkp_compiler/representation_proof.py +466 -0
  313. charm/zkp_compiler/schnorr_proof.py +273 -0
  314. charm/zkp_compiler/thread_safe.py +150 -0
  315. charm/zkp_compiler/zk_demo.py +489 -0
  316. charm/zkp_compiler/zkp_factory.py +330 -0
  317. charm/zkp_compiler/zkp_generator.py +370 -0
  318. charm/zkp_compiler/zkparser.py +269 -0
  319. charm_crypto_framework-0.61.1.dist-info/METADATA +337 -0
  320. charm_crypto_framework-0.61.1.dist-info/RECORD +323 -0
  321. charm_crypto_framework-0.61.1.dist-info/WHEEL +5 -0
  322. charm_crypto_framework-0.61.1.dist-info/licenses/LICENSE.txt +165 -0
  323. charm_crypto_framework-0.61.1.dist-info/top_level.txt +1 -0
charm/toolbox/mta.py ADDED
@@ -0,0 +1,985 @@
1
+ '''
2
+ Multiplicative-to-Additive (MtA) Share Conversion for DKLS23
3
+
4
+ | From: "Threshold ECDSA from ECDSA Assumptions: The Multiparty Case"
5
+ | By: Jack Doerner, Yashvanth Kondi, Eysa Lee, abhi shelat
6
+ | Published: IEEE S&P 2019
7
+ | URL: https://eprint.iacr.org/2019/523
8
+ |
9
+ | Also implements MtAwc (MtA with check) from:
10
+ | "Two-Round Threshold ECDSA from ECDSA Assumptions" (DKLS23)
11
+ | By: Jack Doerner, Yashvanth Kondi, Eysa Lee, abhi shelat
12
+ | Published: IEEE S&P 2023
13
+ | URL: https://eprint.iacr.org/2023/765
14
+
15
+ * type: share conversion
16
+ * setting: Elliptic Curve DDH-hard group
17
+ * assumption: DDH + OT security
18
+
19
+ MtA converts multiplicative shares (a, b) where two parties hold a and b
20
+ to additive shares (alpha, beta) such that a*b = alpha + beta (mod q).
21
+ Neither party learns the other's share.
22
+
23
+ :Authors: Elton de Souza
24
+ :Date: 01/2026
25
+ '''
26
+
27
+ from typing import Dict, List, Tuple, Optional, Any, Union
28
+
29
+ from charm.toolbox.ecgroup import ECGroup, ZR, G
30
+ from charm.toolbox.eccurve import secp256k1
31
+ from charm.toolbox.securerandom import SecureRandomFactory
32
+ from charm.toolbox.ot.base_ot import SimpleOT
33
+ from charm.toolbox.mpc_utils import (
34
+ int_to_bytes,
35
+ bytes_to_int,
36
+ bit_decompose,
37
+ bits_to_int,
38
+ PedersenCommitment,
39
+ )
40
+ import struct
41
+ import hashlib
42
+ import logging
43
+
44
+ # Type aliases for charm-crypto types
45
+ ZRElement = Any # Scalar field element
46
+ GElement = Any # Group/curve point element
47
+ ECGroupType = Any # ECGroup instance
48
+
49
+ # Module logger
50
+ logger = logging.getLogger(__name__)
51
+
52
+
53
+ def hash_to_field(group: ECGroupType, *args: Any) -> ZRElement:
54
+ """
55
+ Hash multiple values to a field element with domain separation.
56
+
57
+ Uses group.hash() for proper domain separation and automatic
58
+ serialization of different types.
59
+
60
+ Parameters
61
+ ----------
62
+ group : ECGroup
63
+ The elliptic curve group
64
+ *args : various
65
+ Values to hash
66
+
67
+ Returns
68
+ -------
69
+ ZR element
70
+ Hash output as field element
71
+ """
72
+ return group.hash((b"MTA_FIELD:",) + args, target_type=ZR)
73
+
74
+
75
+ class CorrelatedOT:
76
+ """
77
+ Correlated Oblivious Transfer for MtA.
78
+
79
+ Generates correlated random values for OT-based MtA.
80
+ For each bit of the sender's input, generates correlation pairs.
81
+ """
82
+
83
+ def __init__(self, groupObj):
84
+ """
85
+ Initialize CorrelatedOT with an elliptic curve group.
86
+
87
+ Parameters
88
+ ----------
89
+ groupObj : ECGroup
90
+ An elliptic curve group object
91
+ """
92
+ self.group = groupObj
93
+ self.order = int(groupObj.order())
94
+ self.rand = SecureRandomFactory.getInstance()
95
+ # Bit length based on group order
96
+ self.bit_length = self.order.bit_length()
97
+
98
+ def generate_correlation(self, delta):
99
+ """
100
+ Generate correlated pair (t0, t1) where t1 = t0 + delta.
101
+
102
+ Parameters
103
+ ----------
104
+ delta : int
105
+ The correlation offset
106
+
107
+ Returns
108
+ -------
109
+ tuple
110
+ (t0, t1) where t1 = t0 + delta (mod order)
111
+ """
112
+ t0 = bytes_to_int(self.rand.getRandomBytes(32)) % self.order
113
+ t1 = (t0 + delta) % self.order
114
+ return (t0, t1)
115
+
116
+ def generate_batch_correlations(self, deltas):
117
+ """
118
+ Generate batch of correlated pairs.
119
+
120
+ Parameters
121
+ ----------
122
+ deltas : list of int
123
+ List of correlation offsets
124
+
125
+ Returns
126
+ -------
127
+ list of tuples
128
+ List of (t0, t1) pairs
129
+ """
130
+ return [self.generate_correlation(d) for d in deltas]
131
+
132
+
133
+ class MtA:
134
+ """
135
+ Multiplicative-to-Additive share conversion using OT.
136
+
137
+ Converts multiplicative shares (a, b) where parties hold a and b
138
+ to additive shares (alpha, beta) where a*b = alpha + beta (mod q).
139
+
140
+ Curve Agnostic
141
+ --------------
142
+ This implementation supports any elliptic curve group that is DDH-hard.
143
+ The curve is specified via the groupObj parameter.
144
+
145
+ The protocol works as follows:
146
+ 1. Sender (holding a) decomposes a into bits
147
+ 2. For each bit position i, run correlated OT with correlation 2^i * b
148
+ 3. Receiver (holding b) chooses based on sender's bits
149
+ 4. Parties compute their additive shares from OT outputs
150
+
151
+ >>> from charm.toolbox.eccurve import secp256k1
152
+ >>> group = ECGroup(secp256k1)
153
+ >>> # Create separate instances for Alice (sender) and Bob (receiver)
154
+ >>> alice_mta = MtA(group)
155
+ >>> bob_mta = MtA(group)
156
+ >>> # Alice has share a, Bob has share b
157
+ >>> a = group.random(ZR)
158
+ >>> b = group.random(ZR)
159
+ >>> # Convert to additive shares using the protocol with real OT
160
+ >>> sender_msg = alice_mta.sender_round1(a)
161
+ >>> receiver_msg, _ = bob_mta.receiver_round1(b, sender_msg)
162
+ >>> alpha, ot_data = alice_mta.sender_round2(receiver_msg)
163
+ >>> beta = bob_mta.receiver_round2(ot_data)
164
+ >>> # Verify: a*b = alpha + beta (mod q)
165
+ >>> product = a * b
166
+ >>> additive_sum = alpha + beta
167
+ >>> product == additive_sum
168
+ True
169
+ """
170
+
171
+ def __init__(self, groupObj: ECGroupType) -> None:
172
+ """
173
+ Initialize MtA with an elliptic curve group.
174
+
175
+ Parameters
176
+ ----------
177
+ groupObj : ECGroup
178
+ An elliptic curve group object from charm.toolbox.ecgroup
179
+
180
+ Raises
181
+ ------
182
+ ValueError
183
+ If groupObj is None
184
+ """
185
+ if groupObj is None:
186
+ raise ValueError("groupObj cannot be None")
187
+ self.group = groupObj
188
+ self.order = int(groupObj.order())
189
+ self.rand = SecureRandomFactory.getInstance()
190
+ self.bit_length = self.order.bit_length()
191
+
192
+ # State variables
193
+ self._a = None
194
+ self._alpha = None
195
+
196
+ def sender_round1(self, a: ZRElement) -> Dict[str, Any]:
197
+ """
198
+ Sender (holding a) generates first message.
199
+
200
+ Sender samples random alpha and prepares OT messages such that
201
+ receiver can learn beta = a*b - alpha. Uses real SimpleOT for security.
202
+
203
+ Parameters
204
+ ----------
205
+ a : ZR element
206
+ Sender's multiplicative share
207
+
208
+ Returns
209
+ -------
210
+ dict
211
+ OT setup parameters containing:
212
+ - 'ot_params': list of OT sender parameters (one per bit position)
213
+ - 'adjustment': integer for receiver to compute beta
214
+ """
215
+ self._a = a
216
+ a_int = int(a) % self.order
217
+
218
+ # Sample random alpha
219
+ alpha_int = bytes_to_int(self.rand.getRandomBytes(32)) % self.order
220
+ self._alpha = alpha_int
221
+
222
+ # OT-based MtA protocol:
223
+ # Goal: alpha + beta = a*b, where sender gets alpha (random), receiver gets beta
224
+ #
225
+ # Receiver has b = sum_i b_i * 2^i
226
+ # For each bit position i, sender prepares two messages:
227
+ # m0_i = r_i (receiver gets this if b_i = 0)
228
+ # m1_i = r_i + a * 2^i (receiver gets this if b_i = 1)
229
+ #
230
+ # After OT, receiver has: sum_i selected_i = sum_i (r_i + b_i * a * 2^i) = r_sum + a*b
231
+ #
232
+ # To get beta = a*b - alpha:
233
+ # beta = sum(selected) - (r_sum + alpha)
234
+ # So sender sends: adjustment = r_sum + alpha
235
+
236
+ # Store OT senders and messages for the transfer phase
237
+ self._ot_senders = []
238
+ self._ot_raw_messages = []
239
+ ot_params_list = []
240
+ r_sum = 0
241
+
242
+ for i in range(self.bit_length):
243
+ # Random mask for this position
244
+ r_i = bytes_to_int(self.rand.getRandomBytes(32)) % self.order
245
+ r_sum = (r_sum + r_i) % self.order
246
+
247
+ # m0 = r_i (receiver gets this if b_i = 0)
248
+ # m1 = r_i + a * 2^i (receiver gets this if b_i = 1)
249
+ power_of_two = (1 << i) % self.order
250
+ m0 = r_i
251
+ m1 = (r_i + a_int * power_of_two) % self.order
252
+
253
+ # Create OT sender instance and setup
254
+ ot_sender = SimpleOT(self.group)
255
+ sender_params = ot_sender.sender_setup()
256
+
257
+ self._ot_senders.append(ot_sender)
258
+ self._ot_raw_messages.append((m0, m1))
259
+ ot_params_list.append(sender_params)
260
+
261
+ # Sender sends r_sum + alpha so receiver can compute beta = sum(selected) - (r_sum + alpha)
262
+ adjustment = (r_sum + alpha_int) % self.order
263
+
264
+ return {
265
+ 'ot_params': ot_params_list,
266
+ 'adjustment': adjustment,
267
+ }
268
+
269
+ def receiver_round1(self, b: ZRElement, sender_msg: Dict[str, Any]) -> Tuple[Dict[str, Any], None]:
270
+ """
271
+ Receiver (holding b) selects OT messages based on bits of b.
272
+
273
+ Uses real SimpleOT: for each bit b_i, receiver only learns m_{b_i}.
274
+ The receiver NEVER sees both m0 and m1.
275
+
276
+ Parameters
277
+ ----------
278
+ b : ZR element
279
+ Receiver's multiplicative share
280
+ sender_msg : dict
281
+ Message from sender_round1
282
+
283
+ Returns
284
+ -------
285
+ tuple (dict, None)
286
+ A tuple containing:
287
+ - dict: Receiver parameters with 'ot_responses' list of OT receiver responses
288
+ - None: Placeholder for beta (computed in receiver_round2)
289
+ """
290
+ ot_params_list = sender_msg['ot_params']
291
+ self._adjustment = sender_msg['adjustment']
292
+
293
+ b_int = int(b) % self.order
294
+ bits_b = bit_decompose(b_int, self.order, len(ot_params_list))
295
+
296
+ # Use real OT to select messages based on bits of b
297
+ # Receiver only learns m_{b_i} for each position - never both messages
298
+ self._ot_receivers = []
299
+ self._ot_receiver_states = []
300
+ ot_responses = []
301
+
302
+ for i, bit in enumerate(bits_b):
303
+ ot_receiver = SimpleOT(self.group)
304
+ receiver_response, receiver_state = ot_receiver.receiver_choose(ot_params_list[i], bit)
305
+
306
+ self._ot_receivers.append(ot_receiver)
307
+ self._ot_receiver_states.append(receiver_state)
308
+ ot_responses.append(receiver_response)
309
+
310
+ # Store bits for compatibility with old interface
311
+ self._bits_b = bits_b
312
+
313
+ return {'ot_responses': ot_responses}, None
314
+
315
+ def sender_round2(self, receiver_msg: Dict[str, Any]) -> Tuple[ZRElement, Dict[str, Any]]:
316
+ """
317
+ Sender processes receiver's OT responses and returns alpha.
318
+
319
+ Parameters
320
+ ----------
321
+ receiver_msg : dict
322
+ Message from receiver_round1 containing OT responses
323
+
324
+ Returns
325
+ -------
326
+ tuple (ZR element, dict)
327
+ A tuple containing:
328
+ - ZR element: Sender's additive share alpha
329
+ - dict: OT data with 'ot_ciphertexts' list for receiver to retrieve
330
+ """
331
+ ot_responses = receiver_msg['ot_responses']
332
+ ot_ciphertexts = []
333
+
334
+ # Complete OT transfer for each bit position
335
+ for i, ot_sender in enumerate(self._ot_senders):
336
+ m0, m1 = self._ot_raw_messages[i]
337
+ # Convert integers to bytes for OT encryption
338
+ m0_bytes = int_to_bytes(m0, 32)
339
+ m1_bytes = int_to_bytes(m1, 32)
340
+ ciphertexts = ot_sender.sender_transfer(ot_responses[i], m0_bytes, m1_bytes)
341
+ ot_ciphertexts.append(ciphertexts)
342
+
343
+ alpha = self.group.init(ZR, self._alpha)
344
+ return alpha, {'ot_ciphertexts': ot_ciphertexts}
345
+
346
+ def receiver_round2(self, sender_round2_msg: Dict[str, Any]) -> ZRElement:
347
+ """
348
+ Receiver retrieves selected OT messages and computes beta.
349
+
350
+ Parameters
351
+ ----------
352
+ sender_round2_msg : dict
353
+ Message from sender_round2 containing OT ciphertexts
354
+
355
+ Returns
356
+ -------
357
+ ZR element
358
+ Receiver's additive share beta such that a*b = alpha + beta (mod q)
359
+ """
360
+ ot_ciphertexts = sender_round2_msg['ot_ciphertexts']
361
+
362
+ # Retrieve selected messages using OT - receiver only gets m_{b_i}
363
+ selected_sum = 0
364
+ for i, ot_receiver in enumerate(self._ot_receivers):
365
+ selected_bytes = ot_receiver.receiver_retrieve(
366
+ ot_ciphertexts[i],
367
+ self._ot_receiver_states[i]
368
+ )
369
+ selected = bytes_to_int(selected_bytes)
370
+ selected_sum = (selected_sum + selected) % self.order
371
+
372
+ # beta = sum(selected) - adjustment = (r_sum + a*b) - (r_sum + alpha) = a*b - alpha
373
+ beta_int = (selected_sum - self._adjustment) % self.order
374
+ self._beta = self.group.init(ZR, beta_int)
375
+
376
+ return self._beta
377
+
378
+ def receiver_complete(self, sender_bits: List[int]) -> ZRElement:
379
+ """
380
+ Receiver returns their additive share beta (already computed).
381
+
382
+ Parameters
383
+ ----------
384
+ sender_bits : list of int
385
+ Sender's bit decomposition (unused in correct protocol)
386
+
387
+ Returns
388
+ -------
389
+ ZR element
390
+ Receiver's additive share beta
391
+ """
392
+ # Beta was already computed in receiver_round1
393
+ return self._beta
394
+
395
+
396
+
397
+ class MtAwc:
398
+ """
399
+ MtA with check - includes ZK proof that conversion is correct.
400
+
401
+ Used for malicious security. Adds commitment and proof phases
402
+ to verify that parties performed MtA correctly.
403
+
404
+ The protocol adds:
405
+ 1. Commitment phase: parties commit to their shares
406
+ 2. Proof phase: parties prove correctness of OT selections
407
+ 3. Verification: parties verify each other's proofs
408
+
409
+ >>> from charm.toolbox.eccurve import secp256k1
410
+ >>> group = ECGroup(secp256k1)
411
+ >>> mta_wc = MtAwc(group)
412
+ >>> # Alice has share a, Bob has share b
413
+ >>> a = group.random(ZR)
414
+ >>> b = group.random(ZR)
415
+ >>> # Run MtA with correctness check
416
+ >>> sender_commit = mta_wc.sender_commit(a)
417
+ >>> receiver_commit = mta_wc.receiver_commit(b)
418
+ >>> # Exchange commitments and run MtA
419
+ >>> sender_msg = mta_wc.sender_round1(a, receiver_commit)
420
+ >>> receiver_msg, _ = mta_wc.receiver_round1(b, sender_commit, sender_msg)
421
+ >>> alpha, sender_proof = mta_wc.sender_round2(receiver_msg)
422
+ >>> beta, valid = mta_wc.receiver_verify(sender_proof)
423
+ >>> valid
424
+ True
425
+ >>> # Verify: a*b = alpha + beta (mod q)
426
+ >>> product = a * b
427
+ >>> additive_sum = alpha + beta
428
+ >>> product == additive_sum
429
+ True
430
+ """
431
+
432
+ def __init__(self, groupObj: ECGroupType) -> None:
433
+ """
434
+ Initialize MtAwc with an elliptic curve group.
435
+
436
+ Parameters
437
+ ----------
438
+ groupObj : ECGroup
439
+ An elliptic curve group object from charm.toolbox.ecgroup
440
+
441
+ Raises
442
+ ------
443
+ ValueError
444
+ If groupObj is None
445
+ """
446
+ if groupObj is None:
447
+ raise ValueError("groupObj cannot be None")
448
+ self.group = groupObj
449
+ self.order = int(groupObj.order())
450
+ self.rand = SecureRandomFactory.getInstance()
451
+ self.bit_length = self.order.bit_length()
452
+ self.mta = MtA(groupObj)
453
+
454
+ # Use centralized PedersenCommitment
455
+ self._pedersen = PedersenCommitment(groupObj)
456
+ self._pedersen.setup()
457
+ self._g = self._pedersen.g
458
+ self._h = self._pedersen.h
459
+
460
+ # State
461
+ self._a = None
462
+ self._b = None
463
+ self._commitment_randomness = None
464
+ self._sender_commit = None
465
+ self._receiver_commit = None
466
+ self._sender_bit_proof = None
467
+
468
+ def _pedersen_commit(self, value: Union[ZRElement, int], randomness: Optional[ZRElement] = None) -> Tuple[GElement, ZRElement]:
469
+ """
470
+ Create Pedersen commitment: C = g^value * h^randomness.
471
+
472
+ Delegates to the centralized PedersenCommitment class.
473
+
474
+ Parameters
475
+ ----------
476
+ value : ZR element or int
477
+ Value to commit to
478
+ randomness : ZR element, optional
479
+ Randomness for commitment (generated if not provided)
480
+
481
+ Returns
482
+ -------
483
+ tuple
484
+ (commitment, randomness)
485
+ """
486
+ return self._pedersen.commit(value, randomness)
487
+
488
+ def _prove_bit_or(self, bit: int, randomness: ZRElement, commitment: GElement) -> Dict[str, Any]:
489
+ """
490
+ Create OR-proof that commitment contains 0 or 1.
491
+
492
+ Uses Schnorr OR-proof (Cramer-Damgard-Schoenmakers technique):
493
+ - Prover knows witness for one branch (the actual bit value)
494
+ - Simulates proof for the other branch
495
+ - Verifier cannot distinguish which branch is real
496
+
497
+ Parameters
498
+ ----------
499
+ bit : int
500
+ The bit value (0 or 1)
501
+ randomness : ZR element
502
+ Randomness used in commitment C = g^bit * h^randomness
503
+ commitment : G element
504
+ The Pedersen commitment to verify
505
+
506
+ Returns
507
+ -------
508
+ dict
509
+ OR-proof containing commitments, challenges, and responses
510
+ """
511
+ g = self._g
512
+ h = self._h
513
+ order = self.order
514
+
515
+ # For C = g^b * h^r, we prove b ∈ {0, 1}
516
+ # If b=0: C = h^r, prove knowledge of r s.t. C = h^r
517
+ # If b=1: C = g * h^r, prove knowledge of r s.t. C/g = h^r
518
+
519
+ # Random values for the real branch
520
+ k = self.group.random(ZR) # Real branch randomness
521
+
522
+ if bit == 0:
523
+ # Real branch: prove C = h^r (b=0)
524
+ # Simulated branch: C/g = h^r' (b=1)
525
+
526
+ # Commit for real branch (b=0)
527
+ A0 = h ** k # Real commitment
528
+
529
+ # Simulate b=1 branch: need (A1, e1, z1) s.t. h^z1 = A1 * (C/g)^e1
530
+ e1 = self.group.random(ZR)
531
+ z1 = self.group.random(ZR)
532
+ C_over_g = commitment * (g ** (-1))
533
+ A1 = (h ** z1) * (C_over_g ** (-int(e1) % order))
534
+
535
+ # Compute challenge e = H(g, h, C, A0, A1)
536
+ challenge_input = (b"OR_PROOF:", g, h, commitment, A0, A1)
537
+ e = self.group.hash(challenge_input, target_type=ZR)
538
+ e_int = int(e) % order
539
+
540
+ # Compute e0 = e - e1 (real challenge)
541
+ e1_int = int(e1) % order
542
+ e0_int = (e_int - e1_int) % order
543
+ e0 = self.group.init(ZR, e0_int)
544
+
545
+ # Compute z0 = k + e0 * r (real response)
546
+ r_int = int(randomness) % order
547
+ k_int = int(k) % order
548
+ z0_int = (k_int + e0_int * r_int) % order
549
+ z0 = self.group.init(ZR, z0_int)
550
+
551
+ else: # bit == 1
552
+ # Real branch: prove C/g = h^r (b=1)
553
+ # Simulated branch: C = h^r' (b=0)
554
+
555
+ # Simulate b=0 branch: need (A0, e0, z0) s.t. h^z0 = A0 * C^e0
556
+ e0 = self.group.random(ZR)
557
+ z0 = self.group.random(ZR)
558
+ A0 = (h ** z0) * (commitment ** (-int(e0) % order))
559
+
560
+ # Commit for real branch (b=1)
561
+ A1 = h ** k # Real commitment
562
+
563
+ # Compute challenge e = H(g, h, C, A0, A1)
564
+ challenge_input = (b"OR_PROOF:", g, h, commitment, A0, A1)
565
+ e = self.group.hash(challenge_input, target_type=ZR)
566
+ e_int = int(e) % order
567
+
568
+ # Compute e1 = e - e0 (real challenge)
569
+ e0_int = int(e0) % order
570
+ e1_int = (e_int - e0_int) % order
571
+ e1 = self.group.init(ZR, e1_int)
572
+
573
+ # Compute z1 = k + e1 * r (real response)
574
+ r_int = int(randomness) % order
575
+ k_int = int(k) % order
576
+ z1_int = (k_int + e1_int * r_int) % order
577
+ z1 = self.group.init(ZR, z1_int)
578
+
579
+ return {
580
+ 'A0': A0,
581
+ 'A1': A1,
582
+ 'e0': e0,
583
+ 'e1': e1,
584
+ 'z0': z0,
585
+ 'z1': z1,
586
+ }
587
+
588
+ def _verify_bit_or(self, commitment: GElement, or_proof: Dict[str, Any]) -> bool:
589
+ """
590
+ Verify OR-proof that commitment contains 0 or 1.
591
+
592
+ Parameters
593
+ ----------
594
+ commitment : G element
595
+ Pedersen commitment to verify
596
+ or_proof : dict
597
+ OR-proof from _prove_bit_or
598
+
599
+ Returns
600
+ -------
601
+ bool
602
+ True if proof is valid, False otherwise
603
+ """
604
+ g = self._g
605
+ h = self._h
606
+ order = self.order
607
+
608
+ A0 = or_proof['A0']
609
+ A1 = or_proof['A1']
610
+ e0 = or_proof['e0']
611
+ e1 = or_proof['e1']
612
+ z0 = or_proof['z0']
613
+ z1 = or_proof['z1']
614
+
615
+ # Verify challenge: e = e0 + e1 = H(g, h, C, A0, A1)
616
+ challenge_input = (b"OR_PROOF:", g, h, commitment, A0, A1)
617
+ e = self.group.hash(challenge_input, target_type=ZR)
618
+ e_int = int(e) % order
619
+ e0_int = int(e0) % order
620
+ e1_int = int(e1) % order
621
+
622
+ if (e0_int + e1_int) % order != e_int:
623
+ return False
624
+
625
+ # Verify b=0 branch: h^z0 = A0 * C^e0
626
+ lhs0 = h ** z0
627
+ rhs0 = A0 * (commitment ** e0)
628
+ if lhs0 != rhs0:
629
+ return False
630
+
631
+ # Verify b=1 branch: h^z1 = A1 * (C/g)^e1
632
+ C_over_g = commitment * (g ** (-1))
633
+ lhs1 = h ** z1
634
+ rhs1 = A1 * (C_over_g ** e1)
635
+ if lhs1 != rhs1:
636
+ return False
637
+
638
+ return True
639
+
640
+ def _prove_bit_decomposition(self, value_int: int, bits: List[int], value_randomness: ZRElement) -> Dict[str, Any]:
641
+ """
642
+ Create ZK proof that bits are valid (0 or 1) and sum to value.
643
+
644
+ Parameters
645
+ ----------
646
+ value_int : int
647
+ The value being decomposed
648
+ bits : list of int
649
+ The bit decomposition (each 0 or 1)
650
+ value_randomness : ZR element
651
+ Randomness used in the value commitment
652
+
653
+ Returns
654
+ -------
655
+ dict
656
+ ZK proof containing:
657
+ - bit_commitments: list of Pedersen commitments to each bit
658
+ - or_proofs: list of OR-proofs that each bit is 0 or 1
659
+ - sum_randomness: combined randomness for sum verification
660
+ """
661
+ bit_commitments = []
662
+ bit_randomness = []
663
+ or_proofs = []
664
+
665
+ # Commit to each bit with fresh randomness
666
+ for i, bit in enumerate(bits):
667
+ r_i = self.group.random(ZR)
668
+ bit_randomness.append(r_i)
669
+ C_i, _ = self._pedersen_commit(bit, r_i)
670
+ bit_commitments.append(C_i)
671
+
672
+ # Generate OR-proof: C_i commits to 0 OR C_i commits to 1
673
+ or_proof = self._prove_bit_or(bit, r_i, C_i)
674
+ or_proofs.append(or_proof)
675
+
676
+ # Sum of bit randomness weighted by powers of 2 should equal value_randomness
677
+ # C = g^value * h^r = ∏ (g^{bit_i * 2^i} * h^{r_i * 2^i})
678
+ # = g^{∑ bit_i * 2^i} * h^{∑ r_i * 2^i}
679
+ # So: r = ∑ r_i * 2^i
680
+ # We provide the sum_randomness_diff = value_randomness - ∑ r_i * 2^i
681
+ # which should be 0 if honest, verifier can check
682
+
683
+ sum_r = 0
684
+ for i, r_i in enumerate(bit_randomness):
685
+ r_i_int = int(r_i) % self.order
686
+ sum_r = (sum_r + r_i_int * (1 << i)) % self.order
687
+
688
+ value_r_int = int(value_randomness) % self.order
689
+ # Diff should be 0 for honest prover
690
+ randomness_diff = (value_r_int - sum_r) % self.order
691
+
692
+ return {
693
+ 'bit_commitments': bit_commitments,
694
+ 'or_proofs': or_proofs,
695
+ 'randomness_diff': randomness_diff,
696
+ }
697
+
698
+ def _verify_bit_decomposition(self, commitment: GElement, proof: Dict[str, Any]) -> bool:
699
+ """
700
+ Verify ZK proof of correct bit decomposition.
701
+
702
+ Parameters
703
+ ----------
704
+ commitment : G element
705
+ Pedersen commitment to the original value
706
+ proof : dict
707
+ Proof from _prove_bit_decomposition
708
+
709
+ Returns
710
+ -------
711
+ bool
712
+ True if proof is valid, False otherwise
713
+ """
714
+ bit_commitments = proof['bit_commitments']
715
+ or_proofs = proof['or_proofs']
716
+ randomness_diff = proof['randomness_diff']
717
+
718
+ if len(bit_commitments) != len(or_proofs):
719
+ return False
720
+
721
+ # 1. Verify each OR-proof (bit is 0 or 1)
722
+ for i, (C_i, or_proof) in enumerate(zip(bit_commitments, or_proofs)):
723
+ if not self._verify_bit_or(C_i, or_proof):
724
+ logger.debug("OR-proof verification failed for bit %d", i)
725
+ return False
726
+
727
+ # 2. Verify sum proof: ∏ C_i^{2^i} * h^{diff} = C
728
+ # If bits sum correctly and randomness is consistent, this should hold
729
+ product = self.group.init(G, 1) # Identity element
730
+ for i, C_i in enumerate(bit_commitments):
731
+ power = 1 << i
732
+ product = product * (C_i ** power)
733
+
734
+ # Account for randomness difference (should be 0 for honest prover)
735
+ if randomness_diff != 0:
736
+ product = product * (self._h ** randomness_diff)
737
+
738
+ if product != commitment:
739
+ logger.debug("Sum verification failed: product != commitment")
740
+ return False
741
+
742
+ return True
743
+
744
+ def sender_commit(self, a: ZRElement) -> Dict[str, Any]:
745
+ """
746
+ Sender commits to share a with ZK bit decomposition proof.
747
+
748
+ Parameters
749
+ ----------
750
+ a : ZR element
751
+ Sender's multiplicative share
752
+
753
+ Returns
754
+ -------
755
+ dict
756
+ Commitment and bit decomposition proof to send to receiver
757
+ """
758
+ self._a = a
759
+ a_int = int(a) % self.order
760
+ commitment, randomness = self._pedersen_commit(a)
761
+ self._commitment_randomness = randomness
762
+ self._sender_commit = commitment
763
+
764
+ # Decompose into bits
765
+ bits = bit_decompose(a_int, self.order, self.bit_length)
766
+
767
+ # Generate ZK proof of correct bit decomposition
768
+ bit_proof = self._prove_bit_decomposition(a_int, bits, randomness)
769
+
770
+ return {
771
+ 'commitment': commitment,
772
+ 'g': self._g,
773
+ 'h': self._h,
774
+ 'bit_proof': bit_proof,
775
+ }
776
+
777
+ def receiver_commit(self, b: ZRElement) -> Dict[str, Any]:
778
+ """
779
+ Receiver commits to share b.
780
+
781
+ Parameters
782
+ ----------
783
+ b : ZR element
784
+ Receiver's multiplicative share
785
+
786
+ Returns
787
+ -------
788
+ dict
789
+ Commitment to send to sender
790
+ """
791
+ self._b = b
792
+ commitment, randomness = self._pedersen_commit(b)
793
+ self._receiver_randomness = randomness
794
+ self._receiver_commit = commitment
795
+
796
+ return {
797
+ 'commitment': commitment,
798
+ }
799
+
800
+ def sender_round1(self, a: ZRElement, receiver_commit: Dict[str, Any]) -> Dict[str, Any]:
801
+ """
802
+ Sender generates first message with receiver's commitment.
803
+
804
+ Parameters
805
+ ----------
806
+ a : ZR element
807
+ Sender's multiplicative share
808
+ receiver_commit : dict
809
+ Receiver's commitment from receiver_commit
810
+
811
+ Returns
812
+ -------
813
+ dict
814
+ Message to send to receiver
815
+ """
816
+ self._a = a
817
+ self._receiver_commit = receiver_commit['commitment']
818
+
819
+ # Run base MtA
820
+ mta_msg = self.mta.sender_round1(a)
821
+
822
+ return {
823
+ 'mta_msg': mta_msg,
824
+ 'sender_commit': self._sender_commit,
825
+ }
826
+
827
+ def receiver_round1(self, b: ZRElement, sender_commit: Dict[str, Any], sender_msg: Dict[str, Any]) -> Tuple[Dict[str, Any], None]:
828
+ """
829
+ Receiver processes sender message with commitments.
830
+
831
+ Parameters
832
+ ----------
833
+ b : ZR element
834
+ Receiver's multiplicative share
835
+ sender_commit : dict
836
+ Sender's commitment from sender_commit (includes bit_proof)
837
+ sender_msg : dict
838
+ Message from sender_round1
839
+
840
+ Returns
841
+ -------
842
+ tuple
843
+ (receiver_message, beta_placeholder)
844
+ """
845
+ self._b = b
846
+ self._g = sender_commit['g']
847
+ self._h = sender_commit['h']
848
+ self._sender_commit = sender_commit['commitment']
849
+ # Store bit decomposition proof for verification in receiver_verify
850
+ self._sender_bit_proof = sender_commit.get('bit_proof')
851
+
852
+ # Run base MtA - now returns (receiver_msg, None) since beta is computed later
853
+ mta_msg = sender_msg['mta_msg']
854
+ receiver_msg, _ = self.mta.receiver_round1(b, mta_msg)
855
+
856
+ # Add proof of correct computation
857
+ # In full implementation, this would include ZK proofs
858
+ return {
859
+ 'mta_msg': receiver_msg,
860
+ 'receiver_commit': self._receiver_commit,
861
+ }, None
862
+
863
+ def sender_round2(self, receiver_msg: Dict[str, Any]) -> Tuple[ZRElement, Dict[str, Any]]:
864
+ """
865
+ Sender completes MtA and generates proof.
866
+
867
+ Parameters
868
+ ----------
869
+ receiver_msg : dict
870
+ Message from receiver_round1
871
+
872
+ Returns
873
+ -------
874
+ tuple
875
+ (alpha, proof) where:
876
+ - alpha: sender's additive share
877
+ - proof: ZK proof of correctness (does NOT reveal sender_bits)
878
+ """
879
+ mta_msg = receiver_msg['mta_msg']
880
+ # New MtA returns (alpha, ot_data) from sender_round2
881
+ alpha, ot_data = self.mta.sender_round2(mta_msg)
882
+
883
+ # Generate commitment-based proof that doesn't reveal the actual bits
884
+ # This proof verifies:
885
+ # 1. The commitment opens correctly
886
+ # 2. The bit decomposition is consistent with the committed value
887
+ # Using a Fiat-Shamir style challenge-response
888
+ a_int = int(self._a) % self.order
889
+
890
+ # Create challenge by hashing public values with domain separation
891
+ challenge_zr = self.group.hash(
892
+ (b"MTA_CHALLENGE:", self._sender_commit, self._g, self._h),
893
+ target_type=ZR
894
+ )
895
+ challenge = self.group.serialize(challenge_zr)
896
+
897
+ # Compute response: s = r + e*a (mod order)
898
+ # where r is the commitment randomness and e is the challenge
899
+ e = int(challenge_zr) % self.order
900
+ r_int = int(self._commitment_randomness) % self.order
901
+ s = (r_int + e * a_int) % self.order
902
+
903
+ # The proof consists of:
904
+ # - The challenge (derived from public values)
905
+ # - The response s
906
+ # - The commitment randomness (for Pedersen opening verification)
907
+ # This does NOT reveal the actual bits of 'a'
908
+ proof = {
909
+ 'challenge': challenge,
910
+ 'response': s,
911
+ 'commitment_randomness': self._commitment_randomness,
912
+ 'ot_data': ot_data, # For receiver to complete OT and get beta
913
+ }
914
+
915
+ return alpha, proof
916
+
917
+ def receiver_verify(self, proof: Dict[str, Any]) -> Tuple[Optional[ZRElement], bool]:
918
+ """
919
+ Receiver verifies proof including ZK bit decomposition and returns beta.
920
+
921
+ Implements full ZK verification per DKLS23 Section 3:
922
+ 1. Verifies challenge-response for commitment
923
+ 2. Verifies bit decomposition OR-proofs (each bit is 0 or 1)
924
+ 3. Verifies bits sum to the committed value
925
+
926
+ Parameters
927
+ ----------
928
+ proof : dict
929
+ Proof from sender_round2
930
+
931
+ Returns
932
+ -------
933
+ tuple
934
+ (beta, valid) where:
935
+ - beta: receiver's additive share
936
+ - valid: boolean indicating if proof is valid
937
+ """
938
+ commitment_randomness = proof['commitment_randomness']
939
+ challenge = proof['challenge']
940
+ response = proof['response']
941
+ ot_data = proof['ot_data']
942
+
943
+ # First, complete the OT to get beta
944
+ beta = self.mta.receiver_round2(ot_data)
945
+ self._beta = beta
946
+
947
+ # Check commitment exists
948
+ if self._sender_commit is None:
949
+ logger.debug("Verification failed: no sender commitment")
950
+ return None, False
951
+
952
+ # Verify the challenge was computed correctly with domain separation
953
+ expected_challenge_zr = self.group.hash(
954
+ (b"MTA_CHALLENGE:", self._sender_commit, self._g, self._h),
955
+ target_type=ZR
956
+ )
957
+ expected_challenge = self.group.serialize(expected_challenge_zr)
958
+
959
+ if challenge != expected_challenge:
960
+ logger.debug("Verification failed: challenge mismatch")
961
+ return None, False
962
+
963
+ # Verify response is in valid range
964
+ if response < 0 or response >= self.order:
965
+ logger.debug("Verification failed: response out of range")
966
+ return None, False
967
+
968
+ # Verify bit decomposition proof (DKLS23 Section 3 ZK verification)
969
+ # This proves that:
970
+ # 1. Each bit is 0 or 1 (via OR-proofs)
971
+ # 2. The bits sum to the committed value
972
+ if self._sender_bit_proof is None:
973
+ logger.debug("Verification failed: no bit decomposition proof")
974
+ return None, False
975
+
976
+ if not self._verify_bit_decomposition(
977
+ self._sender_commit,
978
+ self._sender_bit_proof
979
+ ):
980
+ logger.debug("Verification failed: bit decomposition proof invalid")
981
+ return None, False
982
+
983
+ logger.debug("MtAwc verification successful")
984
+ return self._beta, True
985
+