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
@@ -0,0 +1,589 @@
1
+ '''
2
+ IKNP-style OT Extension for Elliptic Curve Groups
3
+
4
+ | From: "Extending Oblivious Transfers Efficiently"
5
+ | By: Yuval Ishai, Joe Kilian, Kobbi Nissim, Erez Petrank
6
+ | Published: CRYPTO 2003
7
+ | URL: https://www.iacr.org/archive/crypto2003/27290145/27290145.pdf
8
+
9
+ * type: oblivious transfer extension
10
+ * setting: Symmetric primitives (hash, PRG, XOR)
11
+ * assumption: Random Oracle (SHA-256)
12
+
13
+ This module implements OT Extension which allows performing many OTs
14
+ with only a small number of base OT calls (k base OTs for m >> k OTs).
15
+
16
+ :Authors: Elton de Souza
17
+ :Date: 01/2026
18
+ '''
19
+
20
+ from charm.toolbox.securerandom import OpenSSLRand
21
+ from charm.toolbox.bitstring import Bytes
22
+ from charm.toolbox.ot.base_ot import SimpleOT
23
+ from charm.toolbox.symcrypto import AuthenticatedCryptoAbstraction
24
+ import hashlib
25
+ import logging
26
+
27
+ # Module logger
28
+ logger = logging.getLogger(__name__)
29
+
30
+
31
+ def xor_bytes(a, b):
32
+ """
33
+ XOR two byte strings of equal length.
34
+
35
+ Parameters
36
+ ----------
37
+ a : bytes
38
+ First byte string
39
+ b : bytes
40
+ Second byte string
41
+
42
+ Returns
43
+ -------
44
+ bytes
45
+ XOR of the two byte strings
46
+ """
47
+ assert len(a) == len(b), f"xor_bytes: operands differ in length ({len(a)} vs {len(b)})"
48
+ return bytes(x ^ y for x, y in zip(a, b))
49
+
50
+
51
+ def prg(seed, output_length):
52
+ """
53
+ Pseudo-random generator using SHA-256 in counter mode.
54
+
55
+ Expands a seed to output_length bytes using hash chaining.
56
+
57
+ Parameters
58
+ ----------
59
+ seed : bytes
60
+ Random seed bytes
61
+ output_length : int
62
+ Desired output length in bytes
63
+
64
+ Returns
65
+ -------
66
+ bytes
67
+ Pseudo-random bytes of specified length
68
+ """
69
+ output = b''
70
+ counter = 0
71
+ while len(output) < output_length:
72
+ h = hashlib.sha256()
73
+ h.update(b"PRG:") # Domain separator
74
+ h.update(seed)
75
+ h.update(counter.to_bytes(4, 'big'))
76
+ output += h.digest()
77
+ counter += 1
78
+ return output[:output_length]
79
+
80
+
81
+ def hash_to_key(index, value):
82
+ """
83
+ Hash index and value to derive a key for encryption.
84
+
85
+ Parameters
86
+ ----------
87
+ index : int
88
+ OT index
89
+ value : bytes
90
+ Value to hash
91
+
92
+ Returns
93
+ -------
94
+ bytes
95
+ 32-byte key
96
+ """
97
+ h = hashlib.sha256()
98
+ h.update(b"KEY:") # Domain separator
99
+ h.update(index.to_bytes(8, 'big'))
100
+ h.update(value)
101
+ return h.digest()
102
+
103
+
104
+ def transpose_bit_matrix(matrix, rows, cols):
105
+ """
106
+ Transpose a bit matrix represented as a list of byte rows.
107
+
108
+ Parameters
109
+ ----------
110
+ matrix : list of bytes
111
+ Matrix with 'rows' rows, each row being 'cols' bits (cols//8 bytes)
112
+ rows : int
113
+ Number of rows in input matrix
114
+ cols : int
115
+ Number of columns (bits) in input matrix
116
+
117
+ Returns
118
+ -------
119
+ list of bytes
120
+ Transposed matrix with 'cols' rows, each row being 'rows' bits
121
+ """
122
+ # Each row has cols bits = cols//8 bytes
123
+ # Result: cols rows, each with rows bits = rows//8 bytes
124
+ cols_bytes = (cols + 7) // 8
125
+ rows_bytes = (rows + 7) // 8
126
+
127
+ # Initialize result matrix
128
+ result = [bytearray(rows_bytes) for _ in range(cols)]
129
+
130
+ for i in range(rows):
131
+ row_bytes = matrix[i]
132
+ for j in range(cols):
133
+ # Get bit j from row i
134
+ byte_idx = j // 8
135
+ bit_idx = 7 - (j % 8)
136
+ if byte_idx < len(row_bytes):
137
+ bit = (row_bytes[byte_idx] >> bit_idx) & 1
138
+ else:
139
+ bit = 0
140
+
141
+ # Set bit i in column j (which becomes row j in result)
142
+ if bit:
143
+ result_byte_idx = i // 8
144
+ result_bit_idx = 7 - (i % 8)
145
+ result[j][result_byte_idx] |= (1 << result_bit_idx)
146
+
147
+ return [bytes(row) for row in result]
148
+
149
+
150
+ def get_bit(data, bit_index):
151
+ """Get a specific bit from byte array."""
152
+ byte_idx = bit_index // 8
153
+ bit_idx = 7 - (bit_index % 8)
154
+ if byte_idx >= len(data):
155
+ return 0
156
+ return (data[byte_idx] >> bit_idx) & 1
157
+
158
+
159
+ def set_bit(data, bit_index, value):
160
+ """Set a specific bit in a bytearray."""
161
+ byte_idx = bit_index // 8
162
+ bit_idx = 7 - (bit_index % 8)
163
+ if value:
164
+ data[byte_idx] |= (1 << bit_idx)
165
+ else:
166
+ data[byte_idx] &= ~(1 << bit_idx)
167
+
168
+
169
+ class OTExtension:
170
+ """
171
+ IKNP-style OT Extension.
172
+
173
+ Extends k base OTs to m OTs efficiently, where m >> k.
174
+ Uses the matrix transposition trick from the IKNP paper.
175
+
176
+ In the base OT phase, the roles are reversed:
177
+ - The OT Extension receiver acts as sender in base OT
178
+ - The OT Extension sender acts as receiver in base OT
179
+
180
+ The protocol flow is:
181
+
182
+ 1. Base OT Setup (receiver acts as sender, sender acts as receiver):
183
+ - receiver_ext.receiver_setup_base_ots() -> base_ot_setups
184
+ - sender_ext.sender_setup_base_ots()
185
+ - sender_ext.sender_respond_base_ots(base_ot_setups) -> responses
186
+ - receiver_ext.receiver_transfer_seeds(responses) -> ciphertexts
187
+ - sender_ext.sender_receive_seeds(ciphertexts)
188
+
189
+ 2. Extension Phase:
190
+ - sender_ext.sender_init()
191
+ - receiver_ext.receiver_extend(num_ots, choice_bits) -> msg, state
192
+ - sender_ext.sender_extend(num_ots, messages, msg) -> ciphertexts
193
+ - receiver_ext.receiver_output(ciphertexts, state) -> results
194
+ """
195
+
196
+ def __init__(self, groupObj, security_param=128, base_ot=None):
197
+ """
198
+ Initialize OT Extension with an elliptic curve group.
199
+
200
+ Parameters
201
+ ----------
202
+ groupObj : ECGroup
203
+ An elliptic curve group object from charm.toolbox.ecgroup
204
+ security_param : int
205
+ Security parameter (number of base OTs), typically 128
206
+ base_ot : SimpleOT or compatible, optional
207
+ Base OT protocol instance. If None, a SimpleOT instance is created.
208
+ """
209
+ self.group = groupObj
210
+ self.k = security_param # number of base OTs / security parameter
211
+ self.rand = OpenSSLRand()
212
+ self._sender_random_bits = None # s in the protocol
213
+ self._sender_seeds = None # Seeds received from base OT (one per column)
214
+ self._receiver_seed_pairs = None # Seed pairs for receiver (both per column)
215
+ self._base_ot_complete = False
216
+ self._base_ot_states = None # Sender's base OT receiver states
217
+ self._base_ot_instances = None # Receiver's base OT sender instances
218
+ self._sender_ot_instances = None # Sender's base OT receiver instances
219
+
220
+ # Initialize base OT protocol
221
+ if base_ot is None:
222
+ self.base_ot = SimpleOT(groupObj)
223
+ else:
224
+ self.base_ot = base_ot
225
+
226
+ def sender_setup_base_ots(self):
227
+ """
228
+ Sender-side base OT setup (acts as receiver in base OT).
229
+
230
+ Generates random k-bit string s and prepares for k base OTs
231
+ where sender will receive seed_j^{s_j} for each j.
232
+
233
+ Returns
234
+ -------
235
+ dict
236
+ Confirmation that sender is ready for base OT
237
+ """
238
+ # Generate k random bits for s
239
+ k_bytes = (self.k + 7) // 8
240
+ self._sender_random_bits = self.rand.getRandomBytes(k_bytes)
241
+
242
+ # Prepare to receive k base OTs
243
+ self._sender_seeds = [None] * self.k
244
+ self._base_ot_states = [None] * self.k
245
+
246
+ return {'ready': True, 'k': self.k}
247
+
248
+ def receiver_setup_base_ots(self):
249
+ """
250
+ Receiver-side base OT setup (acts as sender in base OT).
251
+
252
+ Generates k pairs of random seeds that will be transferred via base OT.
253
+
254
+ Returns
255
+ -------
256
+ list of dict
257
+ List of k base OT setup messages to send to OT-ext sender
258
+ """
259
+ # Generate k pairs of random seeds
260
+ self._receiver_seed_pairs = []
261
+ base_ot_setups = []
262
+
263
+ for j in range(self.k):
264
+ seed0 = self.rand.getRandomBytes(32)
265
+ seed1 = self.rand.getRandomBytes(32)
266
+ self._receiver_seed_pairs.append((seed0, seed1))
267
+
268
+ # Create a fresh base OT instance for this transfer
269
+ ot_instance = SimpleOT(self.group)
270
+ sender_params = ot_instance.sender_setup()
271
+ base_ot_setups.append({
272
+ 'j': j,
273
+ 'params': sender_params,
274
+ 'ot_instance': ot_instance
275
+ })
276
+
277
+ self._base_ot_instances = [setup['ot_instance'] for setup in base_ot_setups]
278
+ return [{'j': s['j'], 'params': s['params']} for s in base_ot_setups]
279
+
280
+ def sender_respond_base_ots(self, base_ot_setups):
281
+ """
282
+ Sender responds to receiver's base OT setup (acts as receiver, choosing based on s).
283
+
284
+ Parameters
285
+ ----------
286
+ base_ot_setups : list of dict
287
+ Base OT parameters from receiver_setup_base_ots()
288
+
289
+ Returns
290
+ -------
291
+ list of dict
292
+ Receiver responses for each base OT
293
+ """
294
+ responses = []
295
+ self._sender_ot_instances = []
296
+
297
+ for setup in base_ot_setups:
298
+ j = setup['j']
299
+ params = setup['params']
300
+ s_j = get_bit(self._sender_random_bits, j)
301
+
302
+ # Act as receiver in base OT with choice bit s_j
303
+ ot_instance = SimpleOT(self.group)
304
+ response, state = ot_instance.receiver_choose(params, s_j)
305
+
306
+ self._sender_ot_instances.append(ot_instance)
307
+ self._base_ot_states[j] = state
308
+ responses.append({'j': j, 'response': response})
309
+
310
+ return responses
311
+
312
+ def receiver_transfer_seeds(self, sender_responses):
313
+ """
314
+ Receiver completes base OT by transferring seeds.
315
+
316
+ Parameters
317
+ ----------
318
+ sender_responses : list of dict
319
+ Responses from sender_respond_base_ots()
320
+
321
+ Returns
322
+ -------
323
+ list of dict
324
+ Encrypted seed ciphertexts for each base OT
325
+ """
326
+ ciphertexts = []
327
+
328
+ for resp in sender_responses:
329
+ j = resp['j']
330
+ response = resp['response']
331
+ seed0, seed1 = self._receiver_seed_pairs[j]
332
+
333
+ # Transfer both seeds via base OT
334
+ # Convert Bytes objects to native bytes for symcrypto compatibility
335
+ ot_instance = self._base_ot_instances[j]
336
+ cts = ot_instance.sender_transfer(response, bytes(seed0), bytes(seed1))
337
+ ciphertexts.append({'j': j, 'ciphertexts': cts})
338
+
339
+ self._base_ot_complete = True
340
+ return ciphertexts
341
+
342
+ def sender_receive_seeds(self, seed_ciphertexts):
343
+ """
344
+ Sender receives seeds from base OT.
345
+
346
+ Parameters
347
+ ----------
348
+ seed_ciphertexts : list of dict
349
+ Encrypted seeds from receiver_transfer_seeds()
350
+ """
351
+ for ct in seed_ciphertexts:
352
+ j = ct['j']
353
+ ciphertexts = ct['ciphertexts']
354
+ state = self._base_ot_states[j]
355
+
356
+ # Retrieve the seed corresponding to s_j
357
+ ot_instance = self._sender_ot_instances[j]
358
+ seed = ot_instance.receiver_retrieve(ciphertexts, state)
359
+ self._sender_seeds[j] = seed
360
+
361
+ self._base_ot_complete = True
362
+
363
+ def sender_init(self):
364
+ """
365
+ Initialize sender for extension phase.
366
+
367
+ Must be called AFTER base OT setup is complete.
368
+
369
+ Returns
370
+ -------
371
+ dict
372
+ Confirmation that sender is ready (no secrets exposed)
373
+
374
+ Raises
375
+ ------
376
+ RuntimeError
377
+ If base OT setup has not been completed
378
+ """
379
+ if not self._base_ot_complete:
380
+ raise RuntimeError("Base OT setup must be completed before sender_init")
381
+ if self._sender_seeds is None or None in self._sender_seeds:
382
+ raise RuntimeError("Sender seeds not properly received from base OT")
383
+
384
+ return {'ready': True, 'k': self.k}
385
+
386
+ def receiver_extend(self, num_ots, choice_bits):
387
+ """
388
+ Receiver side of the extension protocol.
389
+
390
+ Uses seeds from base OT setup instead of receiving s directly.
391
+
392
+ Parameters
393
+ ----------
394
+ num_ots : int
395
+ Number of OTs to extend to
396
+ choice_bits : bytes
397
+ The receiver's m choice bits (m/8 bytes)
398
+
399
+ Returns
400
+ -------
401
+ tuple
402
+ (message_to_sender, receiver_state)
403
+
404
+ Raises
405
+ ------
406
+ RuntimeError
407
+ If base OT setup has not been completed
408
+ """
409
+ if not self._base_ot_complete:
410
+ raise RuntimeError("Base OT setup must be completed before receiver_extend")
411
+
412
+ m = num_ots
413
+ m_bytes = (m + 7) // 8
414
+
415
+ # Build T matrices from both seed pairs
416
+ T0 = [] # T^0: columns from seed_j^0
417
+ T1 = [] # T^1: columns from seed_j^1
418
+
419
+ for j in range(self.k):
420
+ seed0, seed1 = self._receiver_seed_pairs[j]
421
+ T0.append(prg(seed0, m_bytes))
422
+ T1.append(prg(seed1, m_bytes))
423
+
424
+ # Compute U_j = T_j^0 ⊕ T_j^1 ⊕ choice_bits
425
+ # This ensures: Q_j = T_j^{s_j} when sender computes Q_j = recv_j ⊕ (s_j · U_j)
426
+ U = []
427
+ for j in range(self.k):
428
+ u_j = xor_bytes(xor_bytes(T0[j], T1[j]), choice_bits)
429
+ U.append(u_j)
430
+
431
+ # For receiver's keys: t_i is ALWAYS row i of T^0 transposed
432
+ # This is because:
433
+ # - Sender computes Q_j = T_j^0 ⊕ (s_j · r), so q_i = t_i^0 ⊕ (r_i · s)
434
+ # - If r_i = 0: key0 = H(i, q_i) = H(i, t_i^0), key1 = H(i, t_i^0 ⊕ s)
435
+ # Receiver wants m0, can compute H(i, t_i^0) ✓
436
+ # - If r_i = 1: key0 = H(i, t_i^0 ⊕ s), key1 = H(i, t_i^0)
437
+ # Receiver wants m1, can compute H(i, t_i^0) ✓
438
+ T0_transposed = transpose_bit_matrix(T0, self.k, m)
439
+ t_rows = T0_transposed # Always use T^0
440
+
441
+ receiver_state = {
442
+ 'num_ots': m,
443
+ 'choice_bits': choice_bits,
444
+ 't_rows': t_rows,
445
+ }
446
+
447
+ message_to_sender = {
448
+ 'U': U, # Send U matrix instead of Q
449
+ 'num_ots': m,
450
+ }
451
+
452
+ logger.debug("Receiver extend: m=%d, k=%d", m, self.k)
453
+
454
+ return message_to_sender, receiver_state
455
+
456
+ def sender_extend(self, num_ots, message_pairs, receiver_msg):
457
+ """
458
+ Sender side of the extension protocol.
459
+
460
+ The sender:
461
+ 1. Receives U matrix from receiver
462
+ 2. Computes T from seeds: T_j = PRG(seed_j^{s_j})
463
+ 3. Computes Q_j = T_j ⊕ (s_j · U_j)
464
+ 4. For each i, computes q_i (row i of Q^T)
465
+ 5. Encrypts x_{i,0} with H(i, q_i)
466
+ 6. Encrypts x_{i,1} with H(i, q_i XOR s)
467
+
468
+ Parameters
469
+ ----------
470
+ num_ots : int
471
+ Number of OTs
472
+ message_pairs : list of tuples
473
+ List of (m0, m1) byte message pairs
474
+ receiver_msg : dict
475
+ Message from receiver_extend containing U matrix
476
+
477
+ Returns
478
+ -------
479
+ list of tuples
480
+ List of (y0, y1) encrypted message pairs
481
+
482
+ Raises
483
+ ------
484
+ RuntimeError
485
+ If base OT or sender_init was not completed
486
+ """
487
+ if not self._base_ot_complete:
488
+ raise RuntimeError("Base OT setup must be completed before sender_extend")
489
+ if self._sender_random_bits is None:
490
+ raise RuntimeError("sender_init must be called before sender_extend")
491
+
492
+ m = num_ots
493
+ m_bytes = (m + 7) // 8
494
+ U = receiver_msg['U']
495
+ s = self._sender_random_bits
496
+
497
+ # Build T from received seeds: T_j = PRG(seed_j^{s_j})
498
+ T = []
499
+ for j in range(self.k):
500
+ T.append(prg(self._sender_seeds[j], m_bytes))
501
+
502
+ # Compute Q_j = T_j ⊕ (s_j · U_j)
503
+ Q = []
504
+ for j in range(self.k):
505
+ s_j = get_bit(s, j)
506
+ if s_j == 0:
507
+ Q.append(T[j])
508
+ else:
509
+ Q.append(xor_bytes(T[j], U[j]))
510
+
511
+ # Transpose Q to get q_i for each i
512
+ Q_transposed = transpose_bit_matrix(Q, self.k, m)
513
+
514
+ ciphertexts = []
515
+ for i in range(m):
516
+ q_i = Q_transposed[i]
517
+
518
+ # Key for m0: H(i, q_i)
519
+ key0 = hash_to_key(i, q_i)
520
+
521
+ # Key for m1: H(i, q_i XOR s)
522
+ q_i_xor_s = xor_bytes(q_i, s[:len(q_i)])
523
+ key1 = hash_to_key(i, q_i_xor_s)
524
+
525
+ # Encrypt messages using authenticated encryption (AEAD)
526
+ m0, m1 = message_pairs[i]
527
+
528
+ # Use first 16 bytes of key for AES (AuthenticatedCryptoAbstraction requirement)
529
+ cipher0 = AuthenticatedCryptoAbstraction(key0)
530
+ cipher1 = AuthenticatedCryptoAbstraction(key1)
531
+
532
+ y0 = cipher0.encrypt(m0)
533
+ y1 = cipher1.encrypt(m1)
534
+
535
+ ciphertexts.append((y0, y1))
536
+
537
+ if Q_transposed:
538
+ q0_hex = Q_transposed[0][:8].hex() if len(Q_transposed[0]) >= 8 else Q_transposed[0].hex()
539
+ logger.debug("Sender extend: m=%d, Q_transposed[0][:8]=%s", m, q0_hex)
540
+
541
+ return ciphertexts
542
+
543
+ def receiver_output(self, ciphertexts, receiver_state):
544
+ """
545
+ Receiver decrypts the chosen messages.
546
+
547
+ The receiver uses t_i (from receiver_extend) to decrypt:
548
+ - If r_i = 0: decrypt with H(i, t_i)
549
+ - If r_i = 1: decrypt with H(i, t_i) (which equals H(i, q_i XOR s) for correct choice)
550
+
551
+ Parameters
552
+ ----------
553
+ ciphertexts : list of tuples
554
+ Encrypted message pairs from sender_extend
555
+ receiver_state : dict
556
+ State from receiver_extend
557
+
558
+ Returns
559
+ -------
560
+ list of bytes
561
+ The decrypted chosen messages
562
+ """
563
+ m = receiver_state['num_ots']
564
+ choice_bits = receiver_state['choice_bits']
565
+ t_rows = receiver_state['t_rows']
566
+
567
+ results = []
568
+ for i in range(m):
569
+ t_i = t_rows[i]
570
+ r_i = get_bit(choice_bits, i)
571
+
572
+ # Key: H(i, t_i)
573
+ key = hash_to_key(i, t_i)
574
+
575
+ # Get the ciphertext corresponding to choice
576
+ y0, y1 = ciphertexts[i]
577
+ y = y1 if r_i else y0
578
+
579
+ # Decrypt with authentication
580
+ cipher = AuthenticatedCryptoAbstraction(key)
581
+ try:
582
+ msg = cipher.decrypt(y)
583
+ except ValueError as e:
584
+ raise ValueError(f"Authentication failed for OT index {i}: ciphertext may have been tampered") from e
585
+ results.append(msg)
586
+
587
+ logger.debug("Receiver output: m=%d", m)
588
+
589
+ return results