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,553 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Threshold Secret Sharing for DKLS23 and Threshold ECDSA
|
|
3
|
+
|
|
4
|
+
| From: "How to Share a Secret" (Shamir Secret Sharing)
|
|
5
|
+
| By: Adi Shamir
|
|
6
|
+
| Published: Communications of the ACM, 1979
|
|
7
|
+
| URL: https://dl.acm.org/doi/10.1145/359168.359176
|
|
8
|
+
|
|
|
9
|
+
| Feldman VSS from:
|
|
10
|
+
| "A Practical Scheme for Non-interactive Verifiable Secret Sharing"
|
|
11
|
+
| By: Paul Feldman
|
|
12
|
+
| Published: FOCS 1987
|
|
13
|
+
| URL: https://ieeexplore.ieee.org/document/4568297
|
|
14
|
+
|
|
|
15
|
+
| Pedersen Commitments from:
|
|
16
|
+
| "Non-Interactive and Information-Theoretic Secure Verifiable Secret Sharing"
|
|
17
|
+
| By: Torben Pryds Pedersen
|
|
18
|
+
| Published: CRYPTO 1991
|
|
19
|
+
| URL: https://link.springer.com/chapter/10.1007/3-540-46766-1_9
|
|
20
|
+
|
|
21
|
+
* type: secret sharing
|
|
22
|
+
* setting: Elliptic Curve group
|
|
23
|
+
* assumption: DLP (for Feldman VSS)
|
|
24
|
+
|
|
25
|
+
This module extends Shamir secret sharing for threshold ECDSA requirements,
|
|
26
|
+
providing Feldman VSS, Pedersen commitments, and EC group element support.
|
|
27
|
+
'''
|
|
28
|
+
|
|
29
|
+
from typing import Dict, List, Tuple, Any, Optional
|
|
30
|
+
|
|
31
|
+
from charm.toolbox.ecgroup import ECGroup, ZR, G
|
|
32
|
+
from charm.toolbox.eccurve import secp256k1
|
|
33
|
+
from charm.toolbox.secretshare import SecretShare
|
|
34
|
+
|
|
35
|
+
# Type alias for ZR elements (scalar field elements)
|
|
36
|
+
ZRElement = Any
|
|
37
|
+
# Type alias for G elements (group/curve points)
|
|
38
|
+
GElement = Any
|
|
39
|
+
# Type alias for ECGroup objects
|
|
40
|
+
ECGroupType = Any
|
|
41
|
+
# Type alias for party identifiers
|
|
42
|
+
PartyId = int
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ThresholdSharing:
|
|
46
|
+
"""
|
|
47
|
+
Enhanced secret sharing for threshold ECDSA
|
|
48
|
+
|
|
49
|
+
Supports Feldman VSS and operations on EC groups.
|
|
50
|
+
|
|
51
|
+
Curve Agnostic
|
|
52
|
+
--------------
|
|
53
|
+
This implementation supports any elliptic curve group that is DDH-hard.
|
|
54
|
+
The curve is specified via the groupObj parameter.
|
|
55
|
+
|
|
56
|
+
>>> from charm.toolbox.eccurve import secp256k1
|
|
57
|
+
>>> group = ECGroup(secp256k1)
|
|
58
|
+
>>> ts = ThresholdSharing(group)
|
|
59
|
+
>>> g = group.random(G)
|
|
60
|
+
>>> secret = group.random(ZR)
|
|
61
|
+
>>> shares, commitments = ts.share_with_verification(secret, g, threshold=2, num_parties=3)
|
|
62
|
+
>>> ts.verify_share(1, shares[1], commitments, g)
|
|
63
|
+
True
|
|
64
|
+
>>> ts.verify_share(2, shares[2], commitments, g)
|
|
65
|
+
True
|
|
66
|
+
>>> ts.verify_share(3, shares[3], commitments, g)
|
|
67
|
+
True
|
|
68
|
+
>>> recovered = ts.reconstruct({1: shares[1], 2: shares[2]}, threshold=2)
|
|
69
|
+
>>> secret == recovered
|
|
70
|
+
True
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
def __init__(self, groupObj: ECGroupType) -> None:
|
|
74
|
+
"""
|
|
75
|
+
Initialize threshold sharing with an EC group
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
groupObj: An ECGroup instance (e.g., ECGroup(secp256k1))
|
|
79
|
+
|
|
80
|
+
Raises:
|
|
81
|
+
ValueError: If groupObj is None
|
|
82
|
+
"""
|
|
83
|
+
if groupObj is None:
|
|
84
|
+
raise ValueError("groupObj cannot be None")
|
|
85
|
+
self.group = groupObj
|
|
86
|
+
self.order = groupObj.order()
|
|
87
|
+
|
|
88
|
+
def _eval_polynomial(self, coeffs: List[ZRElement], x: Any) -> ZRElement:
|
|
89
|
+
"""
|
|
90
|
+
Evaluate polynomial at point x using Horner's method
|
|
91
|
+
|
|
92
|
+
This method computes f(x) = a_0 + a_1*x + a_2*x^2 + ... + a_{t-1}*x^{t-1}
|
|
93
|
+
using Horner's method for optimal efficiency.
|
|
94
|
+
|
|
95
|
+
Horner's method rewrites the polynomial as:
|
|
96
|
+
f(x) = a_0 + x*(a_1 + x*(a_2 + ... + x*a_{t-1}))
|
|
97
|
+
|
|
98
|
+
This reduces the number of multiplications from 2n to n-1.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
coeffs: List of coefficients [a_0, a_1, ..., a_{t-1}]
|
|
102
|
+
x: Point to evaluate at (ZR element or int)
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
Polynomial value at x
|
|
106
|
+
"""
|
|
107
|
+
if not coeffs:
|
|
108
|
+
return self.group.init(ZR, 0)
|
|
109
|
+
|
|
110
|
+
if isinstance(x, int):
|
|
111
|
+
x = self.group.init(ZR, x)
|
|
112
|
+
|
|
113
|
+
# Start with the highest degree coefficient
|
|
114
|
+
result = coeffs[-1]
|
|
115
|
+
|
|
116
|
+
# Work backwards through coefficients: result = result * x + a_i
|
|
117
|
+
for i in range(len(coeffs) - 2, -1, -1):
|
|
118
|
+
result = result * x + coeffs[i]
|
|
119
|
+
|
|
120
|
+
return result
|
|
121
|
+
|
|
122
|
+
def share(self, secret: ZRElement, threshold: int, num_parties: int) -> Dict[int, ZRElement]:
|
|
123
|
+
"""
|
|
124
|
+
Basic Shamir secret sharing
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
secret: The secret to share (ZR element)
|
|
128
|
+
threshold: Minimum number of shares needed to reconstruct (t)
|
|
129
|
+
num_parties: Total number of parties (n)
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
Dictionary mapping party_id (1 to n) to share values
|
|
133
|
+
|
|
134
|
+
>>> from charm.toolbox.eccurve import secp256k1
|
|
135
|
+
>>> group = ECGroup(secp256k1)
|
|
136
|
+
>>> ts = ThresholdSharing(group)
|
|
137
|
+
>>> secret = group.random(ZR)
|
|
138
|
+
>>> shares = ts.share(secret, threshold=2, num_parties=4)
|
|
139
|
+
>>> len(shares) == 4
|
|
140
|
+
True
|
|
141
|
+
>>> recovered = ts.reconstruct({1: shares[1], 3: shares[3]}, threshold=2)
|
|
142
|
+
>>> secret == recovered
|
|
143
|
+
True
|
|
144
|
+
"""
|
|
145
|
+
if threshold > num_parties:
|
|
146
|
+
raise ValueError("threshold cannot exceed num_parties")
|
|
147
|
+
if threshold < 1:
|
|
148
|
+
raise ValueError("threshold must be at least 1")
|
|
149
|
+
if threshold > 256:
|
|
150
|
+
raise ValueError(f"Threshold {threshold} exceeds safe limit of 256 for polynomial evaluation")
|
|
151
|
+
|
|
152
|
+
# Generate random polynomial coefficients: a_0 = secret, a_1...a_{t-1} random
|
|
153
|
+
coeffs = [secret]
|
|
154
|
+
for _ in range(threshold - 1):
|
|
155
|
+
coeffs.append(self.group.random(ZR))
|
|
156
|
+
|
|
157
|
+
# Evaluate polynomial at points 1, 2, ..., n
|
|
158
|
+
shares = {}
|
|
159
|
+
for i in range(1, num_parties + 1):
|
|
160
|
+
shares[i] = self._eval_polynomial(coeffs, i)
|
|
161
|
+
|
|
162
|
+
return shares
|
|
163
|
+
|
|
164
|
+
def share_with_verification(self, secret: ZRElement, generator: GElement, threshold: int, num_parties: int) -> Tuple[Dict[int, ZRElement], List[GElement]]:
|
|
165
|
+
"""
|
|
166
|
+
Feldman VSS - shares with public commitments for verification
|
|
167
|
+
|
|
168
|
+
Creates shares using Shamir's scheme and publishes commitments
|
|
169
|
+
C_j = g^{a_j} for each coefficient a_j, allowing verification
|
|
170
|
+
without revealing the secret.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
secret: The secret to share (ZR element)
|
|
174
|
+
generator: Generator point g in the EC group (G element)
|
|
175
|
+
threshold: Minimum shares needed to reconstruct
|
|
176
|
+
num_parties: Total number of parties
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
Tuple of (shares_dict, commitments_list)
|
|
180
|
+
- shares_dict: {party_id: share_value}
|
|
181
|
+
- commitments_list: [C_0, C_1, ..., C_{t-1}] where C_j = g^{a_j}
|
|
182
|
+
|
|
183
|
+
>>> from charm.toolbox.eccurve import secp256k1
|
|
184
|
+
>>> group = ECGroup(secp256k1)
|
|
185
|
+
>>> ts = ThresholdSharing(group)
|
|
186
|
+
>>> g = group.random(G)
|
|
187
|
+
>>> secret = group.random(ZR)
|
|
188
|
+
>>> shares, comms = ts.share_with_verification(secret, g, 2, 3)
|
|
189
|
+
>>> all(ts.verify_share(i, shares[i], comms, g) for i in range(1, 4))
|
|
190
|
+
True
|
|
191
|
+
"""
|
|
192
|
+
if threshold > num_parties:
|
|
193
|
+
raise ValueError("threshold cannot exceed num_parties")
|
|
194
|
+
if threshold < 1:
|
|
195
|
+
raise ValueError("threshold must be at least 1")
|
|
196
|
+
|
|
197
|
+
# Generate polynomial coefficients
|
|
198
|
+
coeffs = [secret]
|
|
199
|
+
for _ in range(threshold - 1):
|
|
200
|
+
coeffs.append(self.group.random(ZR))
|
|
201
|
+
|
|
202
|
+
# Compute Feldman commitments: C_j = g^{a_j}
|
|
203
|
+
commitments = [generator ** coeff for coeff in coeffs]
|
|
204
|
+
|
|
205
|
+
# Generate shares
|
|
206
|
+
shares = {}
|
|
207
|
+
for i in range(1, num_parties + 1):
|
|
208
|
+
shares[i] = self._eval_polynomial(coeffs, i)
|
|
209
|
+
|
|
210
|
+
return shares, commitments
|
|
211
|
+
|
|
212
|
+
def verify_share(self, party_id: int, share: ZRElement, commitments: List[GElement], generator: GElement) -> bool:
|
|
213
|
+
"""
|
|
214
|
+
Verify a share against Feldman commitments
|
|
215
|
+
|
|
216
|
+
Checks that g^{share} == prod_{j=0}^{t-1} C_j^{i^j}
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
party_id: The party's identifier (1 to n)
|
|
220
|
+
share: The share value to verify (ZR element)
|
|
221
|
+
commitments: List of Feldman commitments [C_0, ..., C_{t-1}]
|
|
222
|
+
generator: Generator point g used in commitments
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
True if share is valid, False otherwise
|
|
226
|
+
"""
|
|
227
|
+
# Compute g^{share}
|
|
228
|
+
lhs = generator ** share
|
|
229
|
+
|
|
230
|
+
# Compute prod_{j=0}^{t-1} C_j^{i^j}
|
|
231
|
+
rhs = commitments[0] # C_0^{i^0} = C_0
|
|
232
|
+
i_power = self.group.init(ZR, party_id)
|
|
233
|
+
|
|
234
|
+
for j in range(1, len(commitments)):
|
|
235
|
+
rhs = rhs * (commitments[j] ** i_power)
|
|
236
|
+
i_power = i_power * self.group.init(ZR, party_id)
|
|
237
|
+
|
|
238
|
+
return lhs == rhs
|
|
239
|
+
|
|
240
|
+
def reconstruct(self, shares: Dict[int, ZRElement], threshold: int) -> ZRElement:
|
|
241
|
+
"""
|
|
242
|
+
Reconstruct secret from threshold shares using Lagrange interpolation
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
shares: Dictionary {party_id: share_value} with at least threshold entries
|
|
246
|
+
threshold: The threshold used when sharing
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
The reconstructed secret
|
|
250
|
+
|
|
251
|
+
Raises:
|
|
252
|
+
ValueError: If fewer than threshold shares provided
|
|
253
|
+
|
|
254
|
+
>>> from charm.toolbox.eccurve import secp256k1
|
|
255
|
+
>>> group = ECGroup(secp256k1)
|
|
256
|
+
>>> ts = ThresholdSharing(group)
|
|
257
|
+
>>> secret = group.random(ZR)
|
|
258
|
+
>>> shares = ts.share(secret, threshold=3, num_parties=5)
|
|
259
|
+
>>> recovered = ts.reconstruct({1: shares[1], 2: shares[2], 4: shares[4]}, 3)
|
|
260
|
+
>>> secret == recovered
|
|
261
|
+
True
|
|
262
|
+
"""
|
|
263
|
+
if len(shares) < threshold:
|
|
264
|
+
raise ValueError(f"Need at least {threshold} shares, got {len(shares)}")
|
|
265
|
+
|
|
266
|
+
party_ids = list(shares.keys())
|
|
267
|
+
|
|
268
|
+
# Compute secret = sum of (share_i * lagrange_coeff_i) at x=0
|
|
269
|
+
secret = self.group.init(ZR, 0)
|
|
270
|
+
for i in party_ids:
|
|
271
|
+
coeff = self.lagrange_coefficient(party_ids, i, x=0)
|
|
272
|
+
secret = secret + (shares[i] * coeff)
|
|
273
|
+
|
|
274
|
+
return secret
|
|
275
|
+
|
|
276
|
+
def lagrange_coefficient(self, party_ids: List[int], i: int, x: int = 0) -> ZRElement:
|
|
277
|
+
"""
|
|
278
|
+
Compute Lagrange coefficient for party i at point x
|
|
279
|
+
|
|
280
|
+
L_i(x) = prod_{j != i} (x - j) / (i - j)
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
party_ids: List of party identifiers in the reconstruction set
|
|
284
|
+
i: The party for which to compute the coefficient
|
|
285
|
+
x: The evaluation point (default 0 for secret recovery)
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
The Lagrange coefficient as a ZR element
|
|
289
|
+
|
|
290
|
+
>>> from charm.toolbox.eccurve import secp256k1
|
|
291
|
+
>>> group = ECGroup(secp256k1)
|
|
292
|
+
>>> ts = ThresholdSharing(group)
|
|
293
|
+
>>> coeff = ts.lagrange_coefficient([1, 2, 3], 1, x=0)
|
|
294
|
+
>>> # L_1(0) = (0-2)(0-3) / (1-2)(1-3) = 6/2 = 3
|
|
295
|
+
"""
|
|
296
|
+
if isinstance(x, int):
|
|
297
|
+
x = self.group.init(ZR, x)
|
|
298
|
+
i_zr = self.group.init(ZR, i)
|
|
299
|
+
|
|
300
|
+
result = self.group.init(ZR, 1)
|
|
301
|
+
for j in party_ids:
|
|
302
|
+
if j != i:
|
|
303
|
+
j_zr = self.group.init(ZR, j)
|
|
304
|
+
numerator = x - j_zr
|
|
305
|
+
denominator = i_zr - j_zr
|
|
306
|
+
result = result * numerator * (denominator ** -1)
|
|
307
|
+
|
|
308
|
+
return result
|
|
309
|
+
|
|
310
|
+
def add_shares(self, shares1: Dict[int, ZRElement], shares2: Dict[int, ZRElement]) -> Dict[int, ZRElement]:
|
|
311
|
+
"""
|
|
312
|
+
Add two sets of shares (for additive share combination)
|
|
313
|
+
|
|
314
|
+
Useful for distributed key generation and refreshing.
|
|
315
|
+
|
|
316
|
+
Args:
|
|
317
|
+
shares1: First dictionary of shares {party_id: share}
|
|
318
|
+
shares2: Second dictionary of shares {party_id: share}
|
|
319
|
+
|
|
320
|
+
Returns:
|
|
321
|
+
Dictionary of combined shares
|
|
322
|
+
|
|
323
|
+
>>> from charm.toolbox.eccurve import secp256k1
|
|
324
|
+
>>> group = ECGroup(secp256k1)
|
|
325
|
+
>>> ts = ThresholdSharing(group)
|
|
326
|
+
>>> s1, s2 = group.random(ZR), group.random(ZR)
|
|
327
|
+
>>> shares1 = ts.share(s1, 2, 3)
|
|
328
|
+
>>> shares2 = ts.share(s2, 2, 3)
|
|
329
|
+
>>> combined = ts.add_shares(shares1, shares2)
|
|
330
|
+
>>> recovered = ts.reconstruct({1: combined[1], 2: combined[2]}, 2)
|
|
331
|
+
>>> recovered == s1 + s2
|
|
332
|
+
True
|
|
333
|
+
"""
|
|
334
|
+
if set(shares1.keys()) != set(shares2.keys()):
|
|
335
|
+
raise ValueError("Share sets must have same party IDs")
|
|
336
|
+
|
|
337
|
+
combined = {}
|
|
338
|
+
for party_id in shares1.keys():
|
|
339
|
+
combined[party_id] = shares1[party_id] + shares2[party_id]
|
|
340
|
+
|
|
341
|
+
return combined
|
|
342
|
+
|
|
343
|
+
def refresh_shares(self, shares: Dict[int, ZRElement], threshold: int) -> Dict[int, ZRElement]:
|
|
344
|
+
"""
|
|
345
|
+
Refresh shares for proactive security
|
|
346
|
+
|
|
347
|
+
Generates new shares of zero and adds them to existing shares.
|
|
348
|
+
The new shares reconstruct to the same secret but are unlinkable
|
|
349
|
+
to the old shares.
|
|
350
|
+
|
|
351
|
+
Args:
|
|
352
|
+
shares: Dictionary of current shares {party_id: share}
|
|
353
|
+
threshold: The threshold of the sharing
|
|
354
|
+
|
|
355
|
+
Returns:
|
|
356
|
+
Dictionary of refreshed shares
|
|
357
|
+
|
|
358
|
+
>>> from charm.toolbox.eccurve import secp256k1
|
|
359
|
+
>>> group = ECGroup(secp256k1)
|
|
360
|
+
>>> ts = ThresholdSharing(group)
|
|
361
|
+
>>> secret = group.random(ZR)
|
|
362
|
+
>>> shares = ts.share(secret, 2, 3)
|
|
363
|
+
>>> refreshed = ts.refresh_shares(shares, 2)
|
|
364
|
+
>>> recovered = ts.reconstruct({1: refreshed[1], 3: refreshed[3]}, 2)
|
|
365
|
+
>>> secret == recovered
|
|
366
|
+
True
|
|
367
|
+
"""
|
|
368
|
+
num_parties = len(shares)
|
|
369
|
+
|
|
370
|
+
# Create shares of zero
|
|
371
|
+
zero = self.group.init(ZR, 0)
|
|
372
|
+
zero_shares = self.share(zero, threshold, num_parties)
|
|
373
|
+
|
|
374
|
+
# Remap zero_shares to match party IDs in original shares
|
|
375
|
+
party_ids = sorted(shares.keys())
|
|
376
|
+
remapped_zero_shares = {}
|
|
377
|
+
for idx, party_id in enumerate(party_ids):
|
|
378
|
+
remapped_zero_shares[party_id] = zero_shares[idx + 1]
|
|
379
|
+
|
|
380
|
+
return self.add_shares(shares, remapped_zero_shares)
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
class PedersenVSS(ThresholdSharing):
|
|
384
|
+
"""
|
|
385
|
+
Pedersen VSS with information-theoretic hiding
|
|
386
|
+
|
|
387
|
+
Uses two generators g, h for commitments where the discrete log
|
|
388
|
+
relationship between g and h is unknown. This provides unconditional
|
|
389
|
+
hiding of the secret, unlike Feldman VSS.
|
|
390
|
+
|
|
391
|
+
>>> from charm.toolbox.eccurve import secp256k1
|
|
392
|
+
>>> group = ECGroup(secp256k1)
|
|
393
|
+
>>> pvss = PedersenVSS(group)
|
|
394
|
+
>>> g = group.random(G)
|
|
395
|
+
>>> h = group.random(G)
|
|
396
|
+
>>> secret = group.random(ZR)
|
|
397
|
+
>>> shares, blindings, comms = pvss.share_with_blinding(secret, g, h, 2, 3)
|
|
398
|
+
>>> pvss.verify_pedersen_share(1, shares[1], blindings[1], comms, g, h)
|
|
399
|
+
True
|
|
400
|
+
>>> pvss.verify_pedersen_share(2, shares[2], blindings[2], comms, g, h)
|
|
401
|
+
True
|
|
402
|
+
>>> pvss.verify_pedersen_share(3, shares[3], blindings[3], comms, g, h)
|
|
403
|
+
True
|
|
404
|
+
>>> recovered = pvss.reconstruct({1: shares[1], 2: shares[2]}, 2)
|
|
405
|
+
>>> secret == recovered
|
|
406
|
+
True
|
|
407
|
+
"""
|
|
408
|
+
|
|
409
|
+
def share_with_blinding(self, secret: ZRElement, g: GElement, h: GElement, threshold: int, num_parties: int) -> Tuple[Dict[int, ZRElement], Dict[int, ZRElement], List[GElement]]:
|
|
410
|
+
"""
|
|
411
|
+
Share with Pedersen commitments (information-theoretically hiding)
|
|
412
|
+
|
|
413
|
+
Creates two polynomials:
|
|
414
|
+
- f(x) with f(0) = secret for the actual shares
|
|
415
|
+
- r(x) with r(0) = random blinding for hiding
|
|
416
|
+
|
|
417
|
+
Commitments are C_j = g^{a_j} * h^{b_j} where a_j, b_j are
|
|
418
|
+
coefficients of f and r respectively.
|
|
419
|
+
|
|
420
|
+
Args:
|
|
421
|
+
secret: The secret to share (ZR element)
|
|
422
|
+
g: First generator point
|
|
423
|
+
h: Second generator point (discrete log to g unknown)
|
|
424
|
+
threshold: Minimum shares needed to reconstruct
|
|
425
|
+
num_parties: Total number of parties
|
|
426
|
+
|
|
427
|
+
Returns:
|
|
428
|
+
Tuple of (shares_dict, blindings_dict, commitments_list)
|
|
429
|
+
- shares_dict: {party_id: share_value}
|
|
430
|
+
- blindings_dict: {party_id: blinding_value}
|
|
431
|
+
- commitments_list: [C_0, C_1, ..., C_{t-1}]
|
|
432
|
+
|
|
433
|
+
>>> from charm.toolbox.eccurve import secp256k1
|
|
434
|
+
>>> group = ECGroup(secp256k1)
|
|
435
|
+
>>> pvss = PedersenVSS(group)
|
|
436
|
+
>>> g, h = group.random(G), group.random(G)
|
|
437
|
+
>>> secret = group.random(ZR)
|
|
438
|
+
>>> shares, blindings, comms = pvss.share_with_blinding(secret, g, h, 2, 4)
|
|
439
|
+
>>> all(pvss.verify_pedersen_share(i, shares[i], blindings[i], comms, g, h)
|
|
440
|
+
... for i in range(1, 5))
|
|
441
|
+
True
|
|
442
|
+
"""
|
|
443
|
+
if threshold > num_parties:
|
|
444
|
+
raise ValueError("threshold cannot exceed num_parties")
|
|
445
|
+
if threshold < 1:
|
|
446
|
+
raise ValueError("threshold must be at least 1")
|
|
447
|
+
|
|
448
|
+
# Generate polynomial for secret: f(x) = a_0 + a_1*x + ... + a_{t-1}*x^{t-1}
|
|
449
|
+
secret_coeffs = [secret]
|
|
450
|
+
for _ in range(threshold - 1):
|
|
451
|
+
secret_coeffs.append(self.group.random(ZR))
|
|
452
|
+
|
|
453
|
+
# Generate polynomial for blinding: r(x) = b_0 + b_1*x + ... + b_{t-1}*x^{t-1}
|
|
454
|
+
blinding_coeffs = []
|
|
455
|
+
for _ in range(threshold):
|
|
456
|
+
blinding_coeffs.append(self.group.random(ZR))
|
|
457
|
+
|
|
458
|
+
# Compute Pedersen commitments: C_j = g^{a_j} * h^{b_j}
|
|
459
|
+
commitments = []
|
|
460
|
+
for j in range(threshold):
|
|
461
|
+
C_j = (g ** secret_coeffs[j]) * (h ** blinding_coeffs[j])
|
|
462
|
+
commitments.append(C_j)
|
|
463
|
+
|
|
464
|
+
# Generate shares and blindings
|
|
465
|
+
shares = {}
|
|
466
|
+
blindings = {}
|
|
467
|
+
for i in range(1, num_parties + 1):
|
|
468
|
+
shares[i] = self._eval_polynomial(secret_coeffs, i)
|
|
469
|
+
blindings[i] = self._eval_polynomial(blinding_coeffs, i)
|
|
470
|
+
|
|
471
|
+
return shares, blindings, commitments
|
|
472
|
+
|
|
473
|
+
def verify_pedersen_share(self, party_id: int, share: ZRElement, blinding: ZRElement, commitments: List[GElement], g: GElement, h: GElement) -> bool:
|
|
474
|
+
"""
|
|
475
|
+
Verify a share against Pedersen commitments
|
|
476
|
+
|
|
477
|
+
Checks that g^{share} * h^{blinding} == prod_{j=0}^{t-1} C_j^{i^j}
|
|
478
|
+
|
|
479
|
+
Args:
|
|
480
|
+
party_id: The party's identifier (1 to n)
|
|
481
|
+
share: The share value (ZR element)
|
|
482
|
+
blinding: The blinding value (ZR element)
|
|
483
|
+
commitments: List of Pedersen commitments [C_0, ..., C_{t-1}]
|
|
484
|
+
g: First generator point
|
|
485
|
+
h: Second generator point
|
|
486
|
+
|
|
487
|
+
Returns:
|
|
488
|
+
True if share is valid, False otherwise
|
|
489
|
+
|
|
490
|
+
>>> from charm.toolbox.eccurve import secp256k1
|
|
491
|
+
>>> group = ECGroup(secp256k1)
|
|
492
|
+
>>> pvss = PedersenVSS(group)
|
|
493
|
+
>>> g, h = group.random(G), group.random(G)
|
|
494
|
+
>>> secret = group.random(ZR)
|
|
495
|
+
>>> shares, blindings, comms = pvss.share_with_blinding(secret, g, h, 3, 5)
|
|
496
|
+
>>> pvss.verify_pedersen_share(3, shares[3], blindings[3], comms, g, h)
|
|
497
|
+
True
|
|
498
|
+
"""
|
|
499
|
+
# Compute g^{share} * h^{blinding}
|
|
500
|
+
lhs = (g ** share) * (h ** blinding)
|
|
501
|
+
|
|
502
|
+
# Compute prod_{j=0}^{t-1} C_j^{i^j}
|
|
503
|
+
rhs = commitments[0] # C_0^{i^0} = C_0
|
|
504
|
+
i_power = self.group.init(ZR, party_id)
|
|
505
|
+
|
|
506
|
+
for j in range(1, len(commitments)):
|
|
507
|
+
rhs = rhs * (commitments[j] ** i_power)
|
|
508
|
+
i_power = i_power * self.group.init(ZR, party_id)
|
|
509
|
+
|
|
510
|
+
return lhs == rhs
|
|
511
|
+
|
|
512
|
+
def combine_pedersen_commitments(self, commitments_list: List[List[GElement]]) -> List[GElement]:
|
|
513
|
+
"""
|
|
514
|
+
Combine multiple Pedersen commitments (for DKG)
|
|
515
|
+
|
|
516
|
+
When multiple dealers contribute shares, their commitments
|
|
517
|
+
can be combined element-wise.
|
|
518
|
+
|
|
519
|
+
Args:
|
|
520
|
+
commitments_list: List of commitment lists from different dealers
|
|
521
|
+
|
|
522
|
+
Returns:
|
|
523
|
+
Combined commitments list
|
|
524
|
+
|
|
525
|
+
>>> from charm.toolbox.eccurve import secp256k1
|
|
526
|
+
>>> group = ECGroup(secp256k1)
|
|
527
|
+
>>> pvss = PedersenVSS(group)
|
|
528
|
+
>>> g, h = group.random(G), group.random(G)
|
|
529
|
+
>>> s1, s2 = group.random(ZR), group.random(ZR)
|
|
530
|
+
>>> _, _, comms1 = pvss.share_with_blinding(s1, g, h, 2, 3)
|
|
531
|
+
>>> _, _, comms2 = pvss.share_with_blinding(s2, g, h, 2, 3)
|
|
532
|
+
>>> combined = pvss.combine_pedersen_commitments([comms1, comms2])
|
|
533
|
+
>>> len(combined) == len(comms1)
|
|
534
|
+
True
|
|
535
|
+
"""
|
|
536
|
+
if not commitments_list:
|
|
537
|
+
raise ValueError("Need at least one commitment list")
|
|
538
|
+
|
|
539
|
+
num_coeffs = len(commitments_list[0])
|
|
540
|
+
combined = list(commitments_list[0])
|
|
541
|
+
|
|
542
|
+
for comms in commitments_list[1:]:
|
|
543
|
+
if len(comms) != num_coeffs:
|
|
544
|
+
raise ValueError("All commitment lists must have same length")
|
|
545
|
+
for j in range(num_coeffs):
|
|
546
|
+
combined[j] = combined[j] * comms[j]
|
|
547
|
+
|
|
548
|
+
return combined
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
if __name__ == "__main__":
|
|
552
|
+
import doctest
|
|
553
|
+
doctest.testmod()
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from charm.toolbox.pairinggroup import PairingGroup
|
|
2
|
+
from charm.toolbox.ecgroup import ECGroup
|
|
3
|
+
from charm.toolbox.integergroup import IntegerGroup
|
|
4
|
+
from charm.core.engine.util import bytesToObject,objectToBytes
|
|
5
|
+
from xml.dom.minidom import *
|
|
6
|
+
|
|
7
|
+
def writeToXML(object, groupObj, name=None):
|
|
8
|
+
""" Output
|
|
9
|
+
<?xml version="1.0" ?>
|
|
10
|
+
<charm>
|
|
11
|
+
<group param="SS512" setting="pairing">
|
|
12
|
+
<object>
|
|
13
|
+
This is a test!
|
|
14
|
+
</object>
|
|
15
|
+
</group>
|
|
16
|
+
</charm>
|
|
17
|
+
"""
|
|
18
|
+
# Create the minidom document
|
|
19
|
+
doc = Document()
|
|
20
|
+
|
|
21
|
+
# Create the <wml> base element
|
|
22
|
+
charm = doc.createElement("charm")
|
|
23
|
+
doc.appendChild(charm)
|
|
24
|
+
|
|
25
|
+
# Create the main <card> element
|
|
26
|
+
maingroup = doc.createElement("group")
|
|
27
|
+
# make this programmatic
|
|
28
|
+
setting = groupObj.groupSetting()
|
|
29
|
+
param = groupObj.groupType()
|
|
30
|
+
|
|
31
|
+
maingroup.setAttribute("setting", setting)
|
|
32
|
+
maingroup.setAttribute("param", param)
|
|
33
|
+
charm.appendChild(maingroup)
|
|
34
|
+
|
|
35
|
+
# Create a <p> element
|
|
36
|
+
if name:
|
|
37
|
+
paragraph0 = doc.createElement("name")
|
|
38
|
+
paragraph0.setAttribute("id", name)
|
|
39
|
+
maingroup.appendChild(paragraph0)
|
|
40
|
+
|
|
41
|
+
paragraph1 = doc.createElement("object")
|
|
42
|
+
maingroup.appendChild(paragraph1)
|
|
43
|
+
|
|
44
|
+
# Give the <p> elemenet some text
|
|
45
|
+
# ptext = doc.createTextNode("This is a test!")
|
|
46
|
+
serializedObject = objectToBytes(object, groupObj)
|
|
47
|
+
ptext = doc.createTextNode(bytes.decode(serializedObject, 'utf8'))
|
|
48
|
+
paragraph1.appendChild(ptext)
|
|
49
|
+
|
|
50
|
+
# Print our newly created XML
|
|
51
|
+
print(doc.toprettyxml(indent=" "))
|
|
52
|
+
return doc.toprettyxml(indent=" ")
|
|
53
|
+
|
|
54
|
+
def getText(nodelist):
|
|
55
|
+
rc = []
|
|
56
|
+
for node in nodelist:
|
|
57
|
+
if node.nodeType == node.TEXT_NODE:
|
|
58
|
+
rc.append(node.data)
|
|
59
|
+
result = ''.join(rc)
|
|
60
|
+
return bytes(result, 'utf8')
|
|
61
|
+
|
|
62
|
+
def parseFromXML(xmlObjectString, group=None):
|
|
63
|
+
assert type(xmlObjectString) == str, "Invalid type for XML object"
|
|
64
|
+
dom = parseString(xmlObjectString)
|
|
65
|
+
assert dom.documentElement.tagName == "charm", "Not a Charm element"
|
|
66
|
+
# print(dom.toprettyxml(indent=" "))
|
|
67
|
+
|
|
68
|
+
groupObj = dom.getElementsByTagName("group")
|
|
69
|
+
assert groupObj != None, "Error: could not find group tag."
|
|
70
|
+
groupObj = groupObj[0]
|
|
71
|
+
charmObj1 = dom.getElementsByTagName("object")
|
|
72
|
+
assert charmObj1 != None, "Error: could not find object tag."
|
|
73
|
+
charmObj1 = charmObj1[0]
|
|
74
|
+
|
|
75
|
+
structure = {}
|
|
76
|
+
setting = groupObj.getAttribute("setting")
|
|
77
|
+
param = groupObj.getAttribute("param")
|
|
78
|
+
|
|
79
|
+
charmObj2 = dom.getElementsByTagName("name")
|
|
80
|
+
structure['name'] = None
|
|
81
|
+
if charmObj2 != None:
|
|
82
|
+
charmObj2 = charmObj2[0] # what is this useful for?
|
|
83
|
+
structure['name'] = charmObj2.getAttribute("id")
|
|
84
|
+
|
|
85
|
+
bytesObj = getText(charmObj1.childNodes).strip()
|
|
86
|
+
|
|
87
|
+
if setting == 'pairing' and group == None:
|
|
88
|
+
group = PairingGroup(param)
|
|
89
|
+
elif structure['setting'] == 'elliptic_curve' and group == None:
|
|
90
|
+
group = ECGroup(param)
|
|
91
|
+
elif structure['setting'] == 'integer':
|
|
92
|
+
# TODO: this is a special case
|
|
93
|
+
pass
|
|
94
|
+
return bytesToObject(bytesObj, group)
|