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.
- charm/__init__.py +5 -0
- charm/adapters/__init__.py +0 -0
- charm/adapters/abenc_adapt_hybrid.py +90 -0
- charm/adapters/dabenc_adapt_hybrid.py +145 -0
- charm/adapters/ibenc_adapt_hybrid.py +72 -0
- charm/adapters/ibenc_adapt_identityhash.py +80 -0
- charm/adapters/kpabenc_adapt_hybrid.py +91 -0
- charm/adapters/pkenc_adapt_bchk05.py +121 -0
- charm/adapters/pkenc_adapt_chk04.py +91 -0
- charm/adapters/pkenc_adapt_hybrid.py +98 -0
- charm/adapters/pksig_adapt_naor01.py +89 -0
- charm/config.py +7 -0
- charm/core/__init__.py +0 -0
- charm/core/benchmark/benchmark_util.c +353 -0
- charm/core/benchmark/benchmark_util.h +61 -0
- charm/core/benchmark/benchmarkmodule.c +476 -0
- charm/core/benchmark/benchmarkmodule.h +162 -0
- charm/core/benchmark.cpython-313-darwin.so +0 -0
- charm/core/crypto/AES/AES.c +1464 -0
- charm/core/crypto/AES.cpython-313-darwin.so +0 -0
- charm/core/crypto/DES/DES.c +113 -0
- charm/core/crypto/DES.cpython-313-darwin.so +0 -0
- charm/core/crypto/DES3/DES3.c +26 -0
- charm/core/crypto/DES3.cpython-313-darwin.so +0 -0
- charm/core/crypto/__init__.py +0 -0
- charm/core/crypto/cryptobase/XOR.c +80 -0
- charm/core/crypto/cryptobase/_counter.c +496 -0
- charm/core/crypto/cryptobase/_counter.h +54 -0
- charm/core/crypto/cryptobase/block_template.c +900 -0
- charm/core/crypto/cryptobase/block_template.h +69 -0
- charm/core/crypto/cryptobase/cryptobasemodule.c +220 -0
- charm/core/crypto/cryptobase/libtom/tomcrypt.h +90 -0
- charm/core/crypto/cryptobase/libtom/tomcrypt_argchk.h +44 -0
- charm/core/crypto/cryptobase/libtom/tomcrypt_cfg.h +186 -0
- charm/core/crypto/cryptobase/libtom/tomcrypt_cipher.h +941 -0
- charm/core/crypto/cryptobase/libtom/tomcrypt_custom.h +556 -0
- charm/core/crypto/cryptobase/libtom/tomcrypt_des.c +1912 -0
- charm/core/crypto/cryptobase/libtom/tomcrypt_hash.h +407 -0
- charm/core/crypto/cryptobase/libtom/tomcrypt_mac.h +496 -0
- charm/core/crypto/cryptobase/libtom/tomcrypt_macros.h +435 -0
- charm/core/crypto/cryptobase/libtom/tomcrypt_math.h +534 -0
- charm/core/crypto/cryptobase/libtom/tomcrypt_misc.h +103 -0
- charm/core/crypto/cryptobase/libtom/tomcrypt_pk.h +653 -0
- charm/core/crypto/cryptobase/libtom/tomcrypt_pkcs.h +90 -0
- charm/core/crypto/cryptobase/libtom/tomcrypt_prng.h +199 -0
- charm/core/crypto/cryptobase/stream_template.c +271 -0
- charm/core/crypto/cryptobase/strxor.c +229 -0
- charm/core/crypto/cryptobase.cpython-313-darwin.so +0 -0
- charm/core/engine/__init__.py +5 -0
- charm/core/engine/protocol.py +293 -0
- charm/core/engine/util.py +174 -0
- charm/core/math/__init__.py +0 -0
- charm/core/math/elliptic_curve/ecmodule.c +1986 -0
- charm/core/math/elliptic_curve/ecmodule.h +230 -0
- charm/core/math/elliptic_curve.cpython-313-darwin.so +0 -0
- charm/core/math/elliptic_curve.pyi +63 -0
- charm/core/math/integer/integermodule.c +2539 -0
- charm/core/math/integer/integermodule.h +145 -0
- charm/core/math/integer.cpython-313-darwin.so +0 -0
- charm/core/math/integer.pyi +76 -0
- charm/core/math/pairing/miracl/miracl_config.h +37 -0
- charm/core/math/pairing/miracl/miracl_interface.h +118 -0
- charm/core/math/pairing/miracl/miracl_interface2.h +126 -0
- charm/core/math/pairing/miracl/pairingmodule2.c +2094 -0
- charm/core/math/pairing/miracl/pairingmodule2.h +307 -0
- charm/core/math/pairing/pairingmodule.c +2230 -0
- charm/core/math/pairing/pairingmodule.h +241 -0
- charm/core/math/pairing/relic/pairingmodule3.c +1853 -0
- charm/core/math/pairing/relic/pairingmodule3.h +233 -0
- charm/core/math/pairing/relic/relic_interface.c +1337 -0
- charm/core/math/pairing/relic/relic_interface.h +217 -0
- charm/core/math/pairing/relic/test_relic.c +171 -0
- charm/core/math/pairing.cpython-313-darwin.so +0 -0
- charm/core/math/pairing.pyi +69 -0
- charm/core/utilities/base64.c +248 -0
- charm/core/utilities/base64.h +15 -0
- charm/schemes/__init__.py +0 -0
- charm/schemes/abenc/__init__.py +0 -0
- charm/schemes/abenc/abenc_accountability_jyjxgd20.py +647 -0
- charm/schemes/abenc/abenc_bsw07.py +146 -0
- charm/schemes/abenc/abenc_ca_cpabe_ar17.py +684 -0
- charm/schemes/abenc/abenc_dacmacs_yj14.py +298 -0
- charm/schemes/abenc/abenc_lsw08.py +159 -0
- charm/schemes/abenc/abenc_maabe_rw15.py +236 -0
- charm/schemes/abenc/abenc_maabe_yj14.py +297 -0
- charm/schemes/abenc/abenc_tbpre_lww14.py +309 -0
- charm/schemes/abenc/abenc_unmcpabe_yahk14.py +223 -0
- charm/schemes/abenc/abenc_waters09.py +144 -0
- charm/schemes/abenc/abenc_yct14.py +208 -0
- charm/schemes/abenc/abenc_yllc15.py +178 -0
- charm/schemes/abenc/ac17.py +248 -0
- charm/schemes/abenc/bsw07.py +141 -0
- charm/schemes/abenc/cgw15.py +277 -0
- charm/schemes/abenc/dabe_aw11.py +204 -0
- charm/schemes/abenc/dfa_fe12.py +144 -0
- charm/schemes/abenc/pk_hve08.py +179 -0
- charm/schemes/abenc/waters11.py +143 -0
- charm/schemes/aggrsign_MuSig.py +150 -0
- charm/schemes/aggrsign_bls.py +267 -0
- charm/schemes/blindsig_ps16.py +654 -0
- charm/schemes/chamhash_adm05.py +113 -0
- charm/schemes/chamhash_rsa_hw09.py +100 -0
- charm/schemes/commit/__init__.py +0 -0
- charm/schemes/commit/commit_gs08.py +77 -0
- charm/schemes/commit/commit_pedersen92.py +53 -0
- charm/schemes/encap_bchk05.py +62 -0
- charm/schemes/grpsig/__init__.py +0 -0
- charm/schemes/grpsig/groupsig_bgls04.py +114 -0
- charm/schemes/grpsig/groupsig_bgls04_var.py +115 -0
- charm/schemes/hibenc/__init__.py +0 -0
- charm/schemes/hibenc/hibenc_bb04.py +105 -0
- charm/schemes/hibenc/hibenc_lew11.py +193 -0
- charm/schemes/ibenc/__init__.py +0 -0
- charm/schemes/ibenc/clpkc_rp03.py +119 -0
- charm/schemes/ibenc/ibenc_CW13_z.py +168 -0
- charm/schemes/ibenc/ibenc_bb03.py +94 -0
- charm/schemes/ibenc/ibenc_bf01.py +121 -0
- charm/schemes/ibenc/ibenc_ckrs09.py +120 -0
- charm/schemes/ibenc/ibenc_cllww12_z.py +172 -0
- charm/schemes/ibenc/ibenc_lsw08.py +120 -0
- charm/schemes/ibenc/ibenc_sw05.py +238 -0
- charm/schemes/ibenc/ibenc_waters05.py +144 -0
- charm/schemes/ibenc/ibenc_waters05_z.py +164 -0
- charm/schemes/ibenc/ibenc_waters09.py +107 -0
- charm/schemes/ibenc/ibenc_waters09_z.py +147 -0
- charm/schemes/joye_scheme.py +106 -0
- charm/schemes/lem_scheme.py +207 -0
- charm/schemes/pk_fre_ccv11.py +107 -0
- charm/schemes/pk_vrf.py +127 -0
- charm/schemes/pkenc/__init__.py +0 -0
- charm/schemes/pkenc/pkenc_cs98.py +108 -0
- charm/schemes/pkenc/pkenc_elgamal85.py +122 -0
- charm/schemes/pkenc/pkenc_gm82.py +98 -0
- charm/schemes/pkenc/pkenc_paillier99.py +118 -0
- charm/schemes/pkenc/pkenc_rabin.py +254 -0
- charm/schemes/pkenc/pkenc_rsa.py +186 -0
- charm/schemes/pksig/__init__.py +0 -0
- charm/schemes/pksig/pksig_CW13_z.py +135 -0
- charm/schemes/pksig/pksig_bls04.py +87 -0
- charm/schemes/pksig/pksig_boyen.py +156 -0
- charm/schemes/pksig/pksig_chch.py +97 -0
- charm/schemes/pksig/pksig_chp.py +70 -0
- charm/schemes/pksig/pksig_cl03.py +150 -0
- charm/schemes/pksig/pksig_cl04.py +87 -0
- charm/schemes/pksig/pksig_cllww12_z.py +142 -0
- charm/schemes/pksig/pksig_cyh.py +132 -0
- charm/schemes/pksig/pksig_dsa.py +76 -0
- charm/schemes/pksig/pksig_ecdsa.py +71 -0
- charm/schemes/pksig/pksig_hess.py +104 -0
- charm/schemes/pksig/pksig_hw.py +110 -0
- charm/schemes/pksig/pksig_lamport.py +63 -0
- charm/schemes/pksig/pksig_ps01.py +135 -0
- charm/schemes/pksig/pksig_ps02.py +124 -0
- charm/schemes/pksig/pksig_ps03.py +119 -0
- charm/schemes/pksig/pksig_rsa_hw09.py +206 -0
- charm/schemes/pksig/pksig_schnorr91.py +77 -0
- charm/schemes/pksig/pksig_waters.py +115 -0
- charm/schemes/pksig/pksig_waters05.py +121 -0
- charm/schemes/pksig/pksig_waters09.py +121 -0
- charm/schemes/pre_mg07.py +150 -0
- charm/schemes/prenc/pre_afgh06.py +126 -0
- charm/schemes/prenc/pre_bbs98.py +123 -0
- charm/schemes/prenc/pre_nal16.py +216 -0
- charm/schemes/protocol_a01.py +272 -0
- charm/schemes/protocol_ao00.py +215 -0
- charm/schemes/protocol_cns07.py +274 -0
- charm/schemes/protocol_schnorr91.py +125 -0
- charm/schemes/sigma1.py +64 -0
- charm/schemes/sigma2.py +129 -0
- charm/schemes/sigma3.py +126 -0
- charm/schemes/threshold/__init__.py +59 -0
- charm/schemes/threshold/dkls23_dkg.py +556 -0
- charm/schemes/threshold/dkls23_presign.py +1089 -0
- charm/schemes/threshold/dkls23_sign.py +761 -0
- charm/schemes/threshold/xrpl_wallet.py +967 -0
- charm/test/__init__.py +0 -0
- charm/test/adapters/__init__.py +0 -0
- charm/test/adapters/abenc_adapt_hybrid_test.py +29 -0
- charm/test/adapters/dabenc_adapt_hybrid_test.py +56 -0
- charm/test/adapters/ibenc_adapt_hybrid_test.py +36 -0
- charm/test/adapters/ibenc_adapt_identityhash_test.py +32 -0
- charm/test/adapters/kpabenc_adapt_hybrid_test.py +30 -0
- charm/test/benchmark/abenc_yllc15_bench.py +92 -0
- charm/test/benchmark/benchmark_test.py +148 -0
- charm/test/benchmark_threshold.py +260 -0
- charm/test/conftest.py +38 -0
- charm/test/fuzz/__init__.py +1 -0
- charm/test/fuzz/conftest.py +5 -0
- charm/test/fuzz/fuzz_policy_parser.py +76 -0
- charm/test/fuzz/fuzz_serialization.py +83 -0
- charm/test/schemes/__init__.py +0 -0
- charm/test/schemes/abenc/__init__.py +0 -0
- charm/test/schemes/abenc/abenc_bsw07_test.py +39 -0
- charm/test/schemes/abenc/abenc_dacmacs_yj14_test.py +16 -0
- charm/test/schemes/abenc/abenc_lsw08_test.py +33 -0
- charm/test/schemes/abenc/abenc_maabe_yj14_test.py +16 -0
- charm/test/schemes/abenc/abenc_tbpre_lww14_test.py +16 -0
- charm/test/schemes/abenc/abenc_waters09_test.py +38 -0
- charm/test/schemes/abenc/abenc_yllc15_test.py +74 -0
- charm/test/schemes/chamhash_adm05_test.py +31 -0
- charm/test/schemes/chamhash_rsa_hw09_test.py +29 -0
- charm/test/schemes/commit/__init__.py +0 -0
- charm/test/schemes/commit/commit_gs08_test.py +24 -0
- charm/test/schemes/commit/commit_pedersen92_test.py +26 -0
- charm/test/schemes/dabe_aw11_test.py +45 -0
- charm/test/schemes/encap_bchk05_test.py +21 -0
- charm/test/schemes/grpsig/__init__.py +0 -0
- charm/test/schemes/grpsig/groupsig_bgls04_test.py +35 -0
- charm/test/schemes/grpsig/groupsig_bgls04_var_test.py +39 -0
- charm/test/schemes/hibenc/__init__.py +0 -0
- charm/test/schemes/hibenc/hibenc_bb04_test.py +28 -0
- charm/test/schemes/ibenc/__init__.py +0 -0
- charm/test/schemes/ibenc/ibenc_bb03_test.py +26 -0
- charm/test/schemes/ibenc/ibenc_bf01_test.py +24 -0
- charm/test/schemes/ibenc/ibenc_ckrs09_test.py +25 -0
- charm/test/schemes/ibenc/ibenc_lsw08_test.py +31 -0
- charm/test/schemes/ibenc/ibenc_sw05_test.py +32 -0
- charm/test/schemes/ibenc/ibenc_waters05_test.py +31 -0
- charm/test/schemes/ibenc/ibenc_waters09_test.py +27 -0
- charm/test/schemes/pk_vrf_test.py +29 -0
- charm/test/schemes/pkenc/__init__.py +0 -0
- charm/test/schemes/pkenc_test.py +255 -0
- charm/test/schemes/pksig/__init__.py +0 -0
- charm/test/schemes/pksig_test.py +376 -0
- charm/test/schemes/rsa_alg_test.py +340 -0
- charm/test/schemes/threshold_test.py +1792 -0
- charm/test/serialize/__init__.py +0 -0
- charm/test/serialize/serialize_test.py +40 -0
- charm/test/toolbox/__init__.py +0 -0
- charm/test/toolbox/conversion_test.py +30 -0
- charm/test/toolbox/ecgroup_test.py +53 -0
- charm/test/toolbox/integer_arithmetic_test.py +441 -0
- charm/test/toolbox/paddingschemes_test.py +238 -0
- charm/test/toolbox/policy_parser_stress_test.py +969 -0
- charm/test/toolbox/secretshare_test.py +28 -0
- charm/test/toolbox/symcrypto_test.py +108 -0
- charm/test/toolbox/test_policy_expression.py +16 -0
- charm/test/vectors/__init__.py +1 -0
- charm/test/vectors/test_bls_vectors.py +289 -0
- charm/test/vectors/test_pedersen_vectors.py +315 -0
- charm/test/vectors/test_schnorr_vectors.py +368 -0
- charm/test/zkp_compiler/__init__.py +9 -0
- charm/test/zkp_compiler/benchmark_zkp.py +258 -0
- charm/test/zkp_compiler/test_and_proof.py +240 -0
- charm/test/zkp_compiler/test_batch_verify.py +248 -0
- charm/test/zkp_compiler/test_dleq_proof.py +264 -0
- charm/test/zkp_compiler/test_or_proof.py +231 -0
- charm/test/zkp_compiler/test_proof_serialization.py +121 -0
- charm/test/zkp_compiler/test_range_proof.py +241 -0
- charm/test/zkp_compiler/test_representation_proof.py +325 -0
- charm/test/zkp_compiler/test_schnorr_proof.py +221 -0
- charm/test/zkp_compiler/test_thread_safety.py +169 -0
- charm/test/zkp_compiler/test_zkp_parser.py +139 -0
- charm/toolbox/ABEnc.py +26 -0
- charm/toolbox/ABEncMultiAuth.py +66 -0
- charm/toolbox/ABEnumeric.py +800 -0
- charm/toolbox/Commit.py +24 -0
- charm/toolbox/DFA.py +89 -0
- charm/toolbox/FSA.py +1254 -0
- charm/toolbox/Hash.py +39 -0
- charm/toolbox/IBEnc.py +62 -0
- charm/toolbox/IBSig.py +64 -0
- charm/toolbox/PKEnc.py +66 -0
- charm/toolbox/PKSig.py +56 -0
- charm/toolbox/PREnc.py +32 -0
- charm/toolbox/ZKProof.py +289 -0
- charm/toolbox/__init__.py +0 -0
- charm/toolbox/bitstring.py +49 -0
- charm/toolbox/broadcast.py +220 -0
- charm/toolbox/conversion.py +100 -0
- charm/toolbox/eccurve.py +149 -0
- charm/toolbox/ecgroup.py +143 -0
- charm/toolbox/enum.py +60 -0
- charm/toolbox/hash_module.py +91 -0
- charm/toolbox/integergroup.py +323 -0
- charm/toolbox/iterate.py +22 -0
- charm/toolbox/matrixops.py +76 -0
- charm/toolbox/mpc_utils.py +296 -0
- charm/toolbox/msp.py +175 -0
- charm/toolbox/mta.py +985 -0
- charm/toolbox/node.py +120 -0
- charm/toolbox/ot/__init__.py +22 -0
- charm/toolbox/ot/base_ot.py +374 -0
- charm/toolbox/ot/dpf.py +642 -0
- charm/toolbox/ot/mpfss.py +228 -0
- charm/toolbox/ot/ot_extension.py +589 -0
- charm/toolbox/ot/silent_ot.py +378 -0
- charm/toolbox/paddingschemes.py +423 -0
- charm/toolbox/paddingschemes_test.py +238 -0
- charm/toolbox/pairingcurves.py +85 -0
- charm/toolbox/pairinggroup.py +186 -0
- charm/toolbox/policy_expression_spec.py +70 -0
- charm/toolbox/policytree.py +189 -0
- charm/toolbox/reCompiler.py +346 -0
- charm/toolbox/redundancyschemes.py +65 -0
- charm/toolbox/schemebase.py +188 -0
- charm/toolbox/secretshare.py +104 -0
- charm/toolbox/secretutil.py +174 -0
- charm/toolbox/securerandom.py +73 -0
- charm/toolbox/sigmaprotocol.py +46 -0
- charm/toolbox/specialprimes.py +45 -0
- charm/toolbox/symcrypto.py +279 -0
- charm/toolbox/threshold_sharing.py +553 -0
- charm/toolbox/xmlserialize.py +94 -0
- charm/toolbox/zknode.py +105 -0
- charm/zkp_compiler/__init__.py +89 -0
- charm/zkp_compiler/and_proof.py +460 -0
- charm/zkp_compiler/batch_verify.py +324 -0
- charm/zkp_compiler/dleq_proof.py +423 -0
- charm/zkp_compiler/or_proof.py +305 -0
- charm/zkp_compiler/range_proof.py +417 -0
- charm/zkp_compiler/representation_proof.py +466 -0
- charm/zkp_compiler/schnorr_proof.py +273 -0
- charm/zkp_compiler/thread_safe.py +150 -0
- charm/zkp_compiler/zk_demo.py +489 -0
- charm/zkp_compiler/zkp_factory.py +330 -0
- charm/zkp_compiler/zkp_generator.py +370 -0
- charm/zkp_compiler/zkparser.py +269 -0
- charm_crypto_framework-0.61.1.dist-info/METADATA +337 -0
- charm_crypto_framework-0.61.1.dist-info/RECORD +323 -0
- charm_crypto_framework-0.61.1.dist-info/WHEEL +5 -0
- charm_crypto_framework-0.61.1.dist-info/licenses/LICENSE.txt +165 -0
- charm_crypto_framework-0.61.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Silent OT Extension based on Pseudorandom Correlation Generators
|
|
3
|
+
|
|
4
|
+
| From: "Efficient Pseudorandom Correlation Generators: Silent OT Extension and More"
|
|
5
|
+
| By: Elette Boyle, Geoffroy Couteau, Niv Gilboa, Yuval Ishai, Lisa Kohl, Peter Scholl
|
|
6
|
+
| Published: CRYPTO 2019
|
|
7
|
+
| URL: https://eprint.iacr.org/2019/448
|
|
8
|
+
|
|
9
|
+
* type: oblivious transfer extension
|
|
10
|
+
* setting: pseudorandom correlation generator
|
|
11
|
+
* assumption: LPN, PRG security
|
|
12
|
+
|
|
13
|
+
:Authors: Elton de Souza
|
|
14
|
+
:Date: 01/2026
|
|
15
|
+
'''
|
|
16
|
+
|
|
17
|
+
import hashlib
|
|
18
|
+
import secrets
|
|
19
|
+
import logging
|
|
20
|
+
import math
|
|
21
|
+
from typing import Tuple, List
|
|
22
|
+
|
|
23
|
+
from charm.toolbox.ot.mpfss import MPFSS
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
# Seed serialization version for forward compatibility
|
|
28
|
+
SEED_VERSION = 1
|
|
29
|
+
|
|
30
|
+
# Modulus for arithmetic operations (2^64 for efficiency)
|
|
31
|
+
MODULUS = 1 << 64
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class SilentOT:
|
|
35
|
+
"""
|
|
36
|
+
Silent OT Extension using Pseudorandom Correlation Generators.
|
|
37
|
+
|
|
38
|
+
Generates n pseudo-random OT instances using sublinear communication.
|
|
39
|
+
Uses MPFSS as a building block for compressing sparse vectors.
|
|
40
|
+
|
|
41
|
+
The construction follows Figure 4 from the paper:
|
|
42
|
+
1. GsVOLE generates VOLE correlations from MPFSS
|
|
43
|
+
2. GOT converts VOLE to random OT using correlation-robust hash
|
|
44
|
+
|
|
45
|
+
>>> sot = SilentOT(security_param=128, output_size=64, sparsity=8)
|
|
46
|
+
>>> seed_sender, seed_receiver = sot.gen()
|
|
47
|
+
>>> len(seed_sender) > 0 and len(seed_receiver) > 0
|
|
48
|
+
True
|
|
49
|
+
>>> choice_bits, sender_msgs = sot.expand_sender(seed_sender)
|
|
50
|
+
>>> receiver_msgs = sot.expand_receiver(seed_receiver)
|
|
51
|
+
>>> len(choice_bits) == 64 and len(sender_msgs) == 64
|
|
52
|
+
True
|
|
53
|
+
>>> len(receiver_msgs) == 64
|
|
54
|
+
True
|
|
55
|
+
>>> # Verify OT correlation: sender_msg[i] == receiver_msg[i][choice_bits[i]]
|
|
56
|
+
>>> all(sender_msgs[i] == receiver_msgs[i][choice_bits[i]] for i in range(64))
|
|
57
|
+
True
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
def __init__(self,
|
|
61
|
+
security_param: int = 128,
|
|
62
|
+
output_size: int = 1024,
|
|
63
|
+
sparsity: int = None):
|
|
64
|
+
"""
|
|
65
|
+
Initialize Silent OT.
|
|
66
|
+
|
|
67
|
+
Parameters
|
|
68
|
+
----------
|
|
69
|
+
security_param : int
|
|
70
|
+
Security parameter λ (128 or 256)
|
|
71
|
+
output_size : int
|
|
72
|
+
Number n of OT instances to generate
|
|
73
|
+
sparsity : int
|
|
74
|
+
Parameter t for sparse vector (default: sqrt(n))
|
|
75
|
+
"""
|
|
76
|
+
if security_param not in (128, 256):
|
|
77
|
+
raise ValueError("security_param must be 128 or 256")
|
|
78
|
+
if output_size < 1:
|
|
79
|
+
raise ValueError("output_size must be at least 1")
|
|
80
|
+
|
|
81
|
+
self.security_param = security_param
|
|
82
|
+
self.n = output_size
|
|
83
|
+
# Sparsity parameter t, default to sqrt(n)
|
|
84
|
+
self.t = sparsity if sparsity is not None else max(1, int(math.sqrt(output_size)))
|
|
85
|
+
|
|
86
|
+
# Domain size n' for MPFSS (must be power of 2 >= n)
|
|
87
|
+
self.domain_bits = max(1, (self.n - 1).bit_length())
|
|
88
|
+
self.n_prime = 1 << self.domain_bits
|
|
89
|
+
|
|
90
|
+
# Validate sparsity bounds
|
|
91
|
+
if self.t < 1:
|
|
92
|
+
raise ValueError(f"sparsity must be at least 1, got {self.t}")
|
|
93
|
+
if self.t > self.n_prime:
|
|
94
|
+
raise ValueError(
|
|
95
|
+
f"sparsity ({self.t}) cannot exceed domain size n' ({self.n_prime})"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Initialize MPFSS for function secret sharing
|
|
99
|
+
self._mpfss = MPFSS(security_param=security_param, domain_bits=self.domain_bits)
|
|
100
|
+
|
|
101
|
+
logger.debug("SilentOT initialized: n=%d, t=%d, n'=%d", self.n, self.t, self.n_prime)
|
|
102
|
+
|
|
103
|
+
def _prg(self, seed: bytes, output_length: int) -> bytes:
|
|
104
|
+
"""PRG using SHA-256 in counter mode."""
|
|
105
|
+
output = b''
|
|
106
|
+
counter = 0
|
|
107
|
+
while len(output) < output_length:
|
|
108
|
+
h = hashlib.sha256()
|
|
109
|
+
h.update(seed)
|
|
110
|
+
h.update(counter.to_bytes(4, 'big'))
|
|
111
|
+
output += h.digest()
|
|
112
|
+
counter += 1
|
|
113
|
+
return output[:output_length]
|
|
114
|
+
|
|
115
|
+
def correlation_robust_hash(self, index: int, value: int) -> bytes:
|
|
116
|
+
"""
|
|
117
|
+
Correlation-robust hash H(i, v).
|
|
118
|
+
|
|
119
|
+
Uses SHA-256 as the underlying hash function.
|
|
120
|
+
|
|
121
|
+
Parameters
|
|
122
|
+
----------
|
|
123
|
+
index : int
|
|
124
|
+
OT index
|
|
125
|
+
value : int
|
|
126
|
+
Value to hash (reduced mod 2^64)
|
|
127
|
+
|
|
128
|
+
Returns
|
|
129
|
+
-------
|
|
130
|
+
bytes
|
|
131
|
+
32-byte hash output
|
|
132
|
+
"""
|
|
133
|
+
# Reduce value to 64-bit range for consistent hashing
|
|
134
|
+
value_reduced = value % MODULUS
|
|
135
|
+
h = hashlib.sha256()
|
|
136
|
+
h.update(index.to_bytes(8, 'big'))
|
|
137
|
+
h.update(value_reduced.to_bytes(8, 'big'))
|
|
138
|
+
return h.digest()
|
|
139
|
+
|
|
140
|
+
def gen(self) -> Tuple[bytes, bytes]:
|
|
141
|
+
"""
|
|
142
|
+
Generate PCG seeds for sender (party 0) and receiver (party 1).
|
|
143
|
+
|
|
144
|
+
Implements GsVOLE.Gen from Figure 3 adapted for binary VOLE:
|
|
145
|
+
1. Pick random size-t subset S of [n']
|
|
146
|
+
2. Pick x ∈ F_q as receiver's global delta
|
|
147
|
+
3. Set y[i] = 1 for all i (to get binary u values after compression)
|
|
148
|
+
4. Compute (K_fss_0, K_fss_1) ← MPFSS.Gen(1^λ, f_{S, x·y})
|
|
149
|
+
5. k0 ← (n, n', K_fss_0, S, y, matrix_seed)
|
|
150
|
+
6. k1 ← (n, n', K_fss_1, x, matrix_seed)
|
|
151
|
+
|
|
152
|
+
The VOLE correlation is: w = u * x + v
|
|
153
|
+
Where u ∈ {0,1}^n are the choice bits (sparse after LPN compression).
|
|
154
|
+
|
|
155
|
+
Returns
|
|
156
|
+
-------
|
|
157
|
+
Tuple[bytes, bytes]
|
|
158
|
+
(seed_sender, seed_receiver) - Seeds for both parties
|
|
159
|
+
"""
|
|
160
|
+
# 1. Pick random size-t subset S of [n'] using cryptographically secure sampling
|
|
161
|
+
secure_random = secrets.SystemRandom()
|
|
162
|
+
S = sorted(secure_random.sample(range(self.n_prime), self.t))
|
|
163
|
+
|
|
164
|
+
# 2. Pick random x ∈ F_q as the correlation value (receiver's delta)
|
|
165
|
+
x = secrets.randbelow(MODULUS)
|
|
166
|
+
if x == 0:
|
|
167
|
+
x = 1 # Ensure non-zero for proper correlation
|
|
168
|
+
|
|
169
|
+
# 3. For binary VOLE (to get u ∈ {0,1}), set y[i] = 1
|
|
170
|
+
# This means sparse vector has 1s at positions S, 0 elsewhere
|
|
171
|
+
# After multiplication with random H matrix, we get random values
|
|
172
|
+
# but we'll use a different approach: directly encode choice bits
|
|
173
|
+
y = [1 for _ in range(self.t)]
|
|
174
|
+
|
|
175
|
+
# 3b. Compute x * y element-wise for MPFSS
|
|
176
|
+
# Since y[i] = 1, this is just [x, x, ..., x]
|
|
177
|
+
xy = [x for _ in range(self.t)]
|
|
178
|
+
|
|
179
|
+
# Generate MPFSS keys for function f_{S, x}
|
|
180
|
+
# f_{S,xy}(i) = x if i ∈ S, 0 otherwise
|
|
181
|
+
points = list(zip(S, xy))
|
|
182
|
+
key_fss_0, key_fss_1 = self._mpfss.gen(points)
|
|
183
|
+
|
|
184
|
+
# 4. Serialize seeds
|
|
185
|
+
# Seed for LPN matrix (shared via common random string)
|
|
186
|
+
matrix_seed = secrets.token_bytes(32)
|
|
187
|
+
|
|
188
|
+
# seed_sender = (n, n_prime, t, key_fss_0, S, y, matrix_seed)
|
|
189
|
+
seed_sender = self._serialize_sender_seed(key_fss_0, S, y, matrix_seed)
|
|
190
|
+
|
|
191
|
+
# seed_receiver = (n, n_prime, key_fss_1, x, matrix_seed)
|
|
192
|
+
seed_receiver = self._serialize_receiver_seed(key_fss_1, x, matrix_seed)
|
|
193
|
+
|
|
194
|
+
logger.debug("SilentOT gen: t=%d, |S|=%d, x=%d", self.t, len(S), x)
|
|
195
|
+
|
|
196
|
+
return seed_sender, seed_receiver
|
|
197
|
+
|
|
198
|
+
def _serialize_sender_seed(self, key_fss: bytes, S: List[int],
|
|
199
|
+
y: List[int], matrix_seed: bytes) -> bytes:
|
|
200
|
+
"""Serialize sender's seed."""
|
|
201
|
+
parts = []
|
|
202
|
+
# Version
|
|
203
|
+
parts.append(SEED_VERSION.to_bytes(2, 'big'))
|
|
204
|
+
# Parameters
|
|
205
|
+
parts.append(self.n.to_bytes(4, 'big'))
|
|
206
|
+
parts.append(self.n_prime.to_bytes(4, 'big'))
|
|
207
|
+
parts.append(self.t.to_bytes(4, 'big'))
|
|
208
|
+
# MPFSS key
|
|
209
|
+
parts.append(len(key_fss).to_bytes(4, 'big'))
|
|
210
|
+
parts.append(key_fss)
|
|
211
|
+
# Sparse positions S
|
|
212
|
+
for pos in S:
|
|
213
|
+
parts.append(pos.to_bytes(4, 'big'))
|
|
214
|
+
# y values
|
|
215
|
+
for val in y:
|
|
216
|
+
parts.append(val.to_bytes(8, 'big'))
|
|
217
|
+
# Matrix seed
|
|
218
|
+
parts.append(matrix_seed)
|
|
219
|
+
return b''.join(parts)
|
|
220
|
+
|
|
221
|
+
def _serialize_receiver_seed(self, key_fss: bytes, x: int,
|
|
222
|
+
matrix_seed: bytes) -> bytes:
|
|
223
|
+
"""Serialize receiver's seed."""
|
|
224
|
+
parts = []
|
|
225
|
+
# Version
|
|
226
|
+
parts.append(SEED_VERSION.to_bytes(2, 'big'))
|
|
227
|
+
# Parameters
|
|
228
|
+
parts.append(self.n.to_bytes(4, 'big'))
|
|
229
|
+
parts.append(self.n_prime.to_bytes(4, 'big'))
|
|
230
|
+
parts.append(self.t.to_bytes(4, 'big'))
|
|
231
|
+
# MPFSS key
|
|
232
|
+
parts.append(len(key_fss).to_bytes(4, 'big'))
|
|
233
|
+
parts.append(key_fss)
|
|
234
|
+
# x value
|
|
235
|
+
parts.append(x.to_bytes(8, 'big'))
|
|
236
|
+
# Matrix seed
|
|
237
|
+
parts.append(matrix_seed)
|
|
238
|
+
return b''.join(parts)
|
|
239
|
+
|
|
240
|
+
def _deserialize_sender_seed(self, seed: bytes) -> Tuple:
|
|
241
|
+
"""Deserialize sender's seed."""
|
|
242
|
+
offset = 0
|
|
243
|
+
version = int.from_bytes(seed[offset:offset + 2], 'big')
|
|
244
|
+
offset += 2
|
|
245
|
+
if version != SEED_VERSION:
|
|
246
|
+
raise ValueError(f"Unsupported seed version: {version}, expected {SEED_VERSION}")
|
|
247
|
+
n = int.from_bytes(seed[offset:offset + 4], 'big')
|
|
248
|
+
offset += 4
|
|
249
|
+
n_prime = int.from_bytes(seed[offset:offset + 4], 'big')
|
|
250
|
+
offset += 4
|
|
251
|
+
t = int.from_bytes(seed[offset:offset + 4], 'big')
|
|
252
|
+
offset += 4
|
|
253
|
+
key_len = int.from_bytes(seed[offset:offset + 4], 'big')
|
|
254
|
+
offset += 4
|
|
255
|
+
key_fss = seed[offset:offset + key_len]
|
|
256
|
+
offset += key_len
|
|
257
|
+
S = []
|
|
258
|
+
for _ in range(t):
|
|
259
|
+
S.append(int.from_bytes(seed[offset:offset + 4], 'big'))
|
|
260
|
+
offset += 4
|
|
261
|
+
y = []
|
|
262
|
+
for _ in range(t):
|
|
263
|
+
y.append(int.from_bytes(seed[offset:offset + 8], 'big'))
|
|
264
|
+
offset += 8
|
|
265
|
+
matrix_seed = seed[offset:offset + 32]
|
|
266
|
+
return n, n_prime, t, key_fss, S, y, matrix_seed
|
|
267
|
+
|
|
268
|
+
def _deserialize_receiver_seed(self, seed: bytes) -> Tuple:
|
|
269
|
+
"""Deserialize receiver's seed."""
|
|
270
|
+
offset = 0
|
|
271
|
+
version = int.from_bytes(seed[offset:offset + 2], 'big')
|
|
272
|
+
offset += 2
|
|
273
|
+
if version != SEED_VERSION:
|
|
274
|
+
raise ValueError(f"Unsupported seed version: {version}, expected {SEED_VERSION}")
|
|
275
|
+
n = int.from_bytes(seed[offset:offset + 4], 'big')
|
|
276
|
+
offset += 4
|
|
277
|
+
n_prime = int.from_bytes(seed[offset:offset + 4], 'big')
|
|
278
|
+
offset += 4
|
|
279
|
+
t = int.from_bytes(seed[offset:offset + 4], 'big')
|
|
280
|
+
offset += 4
|
|
281
|
+
key_len = int.from_bytes(seed[offset:offset + 4], 'big')
|
|
282
|
+
offset += 4
|
|
283
|
+
key_fss = seed[offset:offset + key_len]
|
|
284
|
+
offset += key_len
|
|
285
|
+
x = int.from_bytes(seed[offset:offset + 8], 'big')
|
|
286
|
+
offset += 8
|
|
287
|
+
matrix_seed = seed[offset:offset + 32]
|
|
288
|
+
return n, n_prime, t, key_fss, x, matrix_seed
|
|
289
|
+
|
|
290
|
+
def expand_sender(self, seed: bytes) -> Tuple[List[int], List[bytes]]:
|
|
291
|
+
"""
|
|
292
|
+
Expand sender's seed to get (choice_bits, messages).
|
|
293
|
+
|
|
294
|
+
Simplified implementation that works directly on domain points:
|
|
295
|
+
- Sender knows sparse positions S where choice bit = 1
|
|
296
|
+
- For each position i in [n], choice_bit[i] = 1 iff i ∈ S
|
|
297
|
+
- MPFSS shares f where f(i) = x for i ∈ S, 0 otherwise
|
|
298
|
+
- v0[i] is sender's share, with v0[i] + v1[i] = f(i)
|
|
299
|
+
|
|
300
|
+
OT correlation:
|
|
301
|
+
- Sender outputs: (choice_bits, messages) where message[i] = H(i, -v0[i])
|
|
302
|
+
- Receiver outputs: (m0, m1) where m0 = H(i, v1[i]), m1 = H(i, v1[i] - x)
|
|
303
|
+
- When choice=0 (i ∉ S): f(i)=0, so v0+v1=0, thus -v0=v1, so sender_msg = m0 ✓
|
|
304
|
+
- When choice=1 (i ∈ S): f(i)=x, so v0+v1=x, thus -v0=v1-x, so sender_msg = m1 ✓
|
|
305
|
+
|
|
306
|
+
Parameters
|
|
307
|
+
----------
|
|
308
|
+
seed : bytes
|
|
309
|
+
Sender's seed from gen()
|
|
310
|
+
|
|
311
|
+
Returns
|
|
312
|
+
-------
|
|
313
|
+
Tuple[List[int], List[bytes]]
|
|
314
|
+
(choice_bits, messages) where:
|
|
315
|
+
- choice_bits[i] is 0 or 1
|
|
316
|
+
- messages[i] is 32-byte message
|
|
317
|
+
"""
|
|
318
|
+
n, n_prime, t, key_fss, S, y, matrix_seed = self._deserialize_sender_seed(seed)
|
|
319
|
+
|
|
320
|
+
# Compute MPFSS full evaluation: v0 (sender's share)
|
|
321
|
+
v0 = self._mpfss.full_eval(0, key_fss)
|
|
322
|
+
|
|
323
|
+
# Choice bits: 1 for positions in S, 0 otherwise
|
|
324
|
+
S_set = set(S)
|
|
325
|
+
choice_bits = [1 if i in S_set else 0 for i in range(self.n)]
|
|
326
|
+
|
|
327
|
+
# Messages: H(i, -v0[i]) for each i
|
|
328
|
+
# This matches receiver's m_{choice[i]} due to MPFSS correlation
|
|
329
|
+
messages = []
|
|
330
|
+
for i in range(self.n):
|
|
331
|
+
neg_v0_i = (-v0[i]) % MODULUS
|
|
332
|
+
msg = self.correlation_robust_hash(i, neg_v0_i)
|
|
333
|
+
messages.append(msg)
|
|
334
|
+
|
|
335
|
+
logger.debug("SilentOT expand_sender: n=%d, sum(choice_bits)=%d",
|
|
336
|
+
self.n, sum(choice_bits))
|
|
337
|
+
|
|
338
|
+
return choice_bits, messages
|
|
339
|
+
|
|
340
|
+
def expand_receiver(self, seed: bytes) -> List[Tuple[bytes, bytes]]:
|
|
341
|
+
"""
|
|
342
|
+
Expand receiver's seed to get both messages for each OT.
|
|
343
|
+
|
|
344
|
+
Simplified implementation:
|
|
345
|
+
- Receiver has MPFSS key that shares f where f(i) = x for i ∈ S, 0 otherwise
|
|
346
|
+
- v1[i] is receiver's share, with v0[i] + v1[i] = f(i)
|
|
347
|
+
|
|
348
|
+
For each OT i:
|
|
349
|
+
- m0 = H(i, v1[i]) -- matches sender when choice=0 (f(i)=0, v1=-v0)
|
|
350
|
+
- m1 = H(i, v1[i] - x) -- matches sender when choice=1 (f(i)=x, v1-x=-v0)
|
|
351
|
+
|
|
352
|
+
Parameters
|
|
353
|
+
----------
|
|
354
|
+
seed : bytes
|
|
355
|
+
Receiver's seed from gen()
|
|
356
|
+
|
|
357
|
+
Returns
|
|
358
|
+
-------
|
|
359
|
+
List[Tuple[bytes, bytes]]
|
|
360
|
+
List of (m0, m1) tuples for each OT
|
|
361
|
+
"""
|
|
362
|
+
n, n_prime, t, key_fss, x, matrix_seed = self._deserialize_receiver_seed(seed)
|
|
363
|
+
|
|
364
|
+
# Compute MPFSS full evaluation: v1 (receiver's share)
|
|
365
|
+
v1 = self._mpfss.full_eval(1, key_fss)
|
|
366
|
+
|
|
367
|
+
# Generate both messages for each OT
|
|
368
|
+
messages = []
|
|
369
|
+
for i in range(self.n):
|
|
370
|
+
# m0 = H(i, v1[i]) - for when choice = 0
|
|
371
|
+
m0 = self.correlation_robust_hash(i, v1[i])
|
|
372
|
+
# m1 = H(i, v1[i] - x) - for when choice = 1
|
|
373
|
+
m1 = self.correlation_robust_hash(i, (v1[i] - x) % MODULUS)
|
|
374
|
+
messages.append((m0, m1))
|
|
375
|
+
|
|
376
|
+
logger.debug("SilentOT expand_receiver: n=%d", self.n)
|
|
377
|
+
|
|
378
|
+
return messages
|