The protocol uses a linearly homomorphic encryption scheme using class groups based on CL15's work to compute encrypted signature from the encrypted ECDSA signing key.
The encryption scheme has multiple advantageous features:
- The message space can be adjusted to any prime, or a power of prime which is suitable for ECDSA circuit computation.
- Because of the correct message space, we don't require additional range proofs.
- The construction of keys for the scheme does require any secret primes.
The scheme is constructed via class groups of unknown order which can be created with a subgroup (F) where the discrete log problem is easy to solve. The other subgroup (H) has an unknown order where the discrete log problem is hard.
This ECDSA signature generation protocol consists of three rounds. During each round, a constant amount of data is transmitted by each node. Consequently, the communication overhead remains constant (O(1)
) regardless of the number of participating nodes. In terms of computation, all nodes execute their computations in parallel and each node processes a constant amount of data, resulting in a per-party computation complexity of O(1)
but for non-interactiveness property, each node has to verify the data generated by all the other nodes, which makes the computation complexity O(n)
per party.
We use the CL-HSMq scheme introduced in https://eprint.iacr.org/2018/791 which has an adjustable message space of an odd prime q
, but also variant modulo q^k
and product of primes as analysed in DJS19. We define q
to be the order of SECP256K1
curve to solve ECDSA circuit in encrypted form. We use the C++ class group implementation BICYCL for our PoC which also support CL-HSM2k construction described in https://eprint.iacr.org/2022/1143 (although not required for us).
The scheme describes the following functions:
- Sample private decryption key
sk
randomly in the private key bounds defined by the public parameters. - Compute public encryption key
pk = h ^ sk
.
- Sample a random number
r
in the private key bounds. - Compute
c1 = h ^ r
andc2 = f^m * pk^r
. - Return
(c1, c2)
as the ciphertext.
Given the public key pk
and encrypted messages e1
and e2
, perform the following:
- Parse
(c1, c2) <- e1
and(c1', c2') <- e2
. - Compute
c1'' = c1 * c1'
andc2'' = c2 * c2'
. - Sample a random number
r
in the private key bounds. - Return
e = e1 + e2 = (c1'' * h^r, c2'' * pk^r)
.
Given the public key pk
, encrypted message e
and a scalar a
, perform the following:
- Parse
(c1, c2) <- e
. - Compute
c1' = c1 ^ a
andc2' = c2 ^ a
. - Sample a random number
r
in the private key bounds. - Return
a * e = (c1' * h^r, c2' * pk^r)
.
To support threshold decryption of an encrypted message, we use two more functions, partDec()
and aggPartDecs()
.
Given an encrypted message e
and a decryption key additive share sk_i
. (Additive share can be obtained from the shamir integer share by multiplying with the appropriate lagrange coefficient), perform the following:
- Parse
(c1, c2) <- e
. - Compute the partial decryption
d_i = c1 ^ (-sk_i)
and return this value.
Given an encrypted message e
and a set of threshold number of partial decryptions [d_i
], perform the following:
- Parse
(c1, c2) <- e
. - Compute
M = c2 * product(d_i)
. - Return
log_f(M)
ifM
is inF
else return null.
We require the following proofs in the protocol:
- Proof of knowledge of discrete log. (knowledge of
k
givenk.G
) - Proof of plaintext knowledge and correct multiplication.
- Proof of encryption of discrete log. (prove knowledge of
k
and thatk
used inenc(k)
andk.G
is same). - Proof of correct threshold decryption.
We require these proofs to achieve the identifiable abort property which allows us to detect the malicious or corrupt nodes and prevent them from further performing DoS attacks against the signature generation process. Some proofs might be redundant and the exact proofs need to be finalised.
The paper https://eprint.iacr.org/2021/205 provides the design for 1st and 3rd proofs and the paper https://eprint.iacr.org/2022/1437 provides the design for the 2nd proof. The design for 4th proof can be constructed from the above proof systems after only a slight modification.
The reconstruction or the usage of shamir shares involves division in the lagrange coefficients which are not supported groups with unknown order. The following protocol by https://eprint.iacr.org/2022/1437 is used to share secret integers:
- To distribute the secret
s
, create polynomialF(x) = n! * s + a_{1} * x + ... + a_{t-1} * x^(t - 1)
. (Here,n!
is multiplied to the integers
to prevent the leakages mod i
whenF(i)
is shared). - Share
F(i)
for alli
in set of participant indices. - To reconstruct, get
t
or more shares and return(n!)^2 * s = sum(n! * L_i * F(i))
whereL_i
is the lagrange coefficient. It is multiplied withn!
to cancel out the denominator.
Notes
The reconstruction returns
(n!)^2 * s
instead ofs
.
Other coefficients in the polynomial should be chosen from a large enough interval to hide a. The range mentioned by https://www.ndss-symposium.org/ndss-paper/secure-multiparty-computation-of-threshold-signatures-made-more-efficient/ is
[0, 2^(log2(B) + 1 + 2logt + nlogn + λd)]
whereB
is the secret key bound of class group parameters.
To share class group key shares which are of length (n^t)*2^(log2(B) + 1 + 2logt + nlogn + λd)
using the encryption scheme with message space q
where q
is the order of curve SECP256K1
, we have to perform chunking of the shares.
- Decompose share
X
in baseq
such thatX = x_k * q^k + x_{k-1} * q^(k-1) + ... + x_1 * q + x_0
. This decomposition can be done by performing repeated modq
and integer division. - Return the chunks
[x_k, x_{k-1}, ..., x_1, x_0]
.
We require the distribution of class group decryption key among participants so that they can perform threshold decryption of an encrypted message using their share of the decryption key.
We refer the 2 round DKG protocol described in https://www.ndss-symposium.org/ndss-paper/secure-multiparty-computation-of-threshold-signatures-made-more-efficient/ to prevent public key biasing.
Note: Pederson commitments are used in class groups.
All n
users perform the following:
- Sample two random secret values
X_i
andX'_i
in the secretkey bounds of CL cryptosystem.X'_i
's shares are used as hiding factors in Pederson commitments. - Compute
n
shares using the integer secret sharing of the above two secrets. Thej
-th shares are represented asX_ij
andX'_ij
. - For each share
X_ij
, compute it's Pederson commitment usingX'_ij
asPC_ij
. - Compute
PK_ij = h^(X_ij * n!)
and encrypt this class group element usingj
-th participant's encryption key to getC-PK_ij
. Hereh
is the generator of subgroup of unknown order. - Compute chunks of all shares
X_ij
and encrypt them usingj
-th participant's encryption key. - Compute the proof
P_ij
to prove the usage of sameX_ij
in the chunks andPC_ij
. - Broadcast the proof, encrypted values and Pederson commitments.
At the end, the n
users peform the following:
- Compute duals for all participant indices.
- Perfrom dual-code verification on all the Pederson commitments
PC_ij
in class groups. - Verify all the proofs
P_ij
. - Remove the participants for whose data, the verification fails.
All n
users perform the following:
- Get all the encrypted chunked shares from
j
parties, and decrypt the chunks. - Combine the chunks to get
j
shares and add all shares to get one sharex_i
for self. - Compute
Pub-x_i = h^(x_i * n!)
. - Combine all
C-PK_ij
to getC-PK_i
using homomorphic addition. - Compute proof of knowledge of
x_i
and correctPub-x_i
andC-PK_i
. - Broadcast the proof,
Pub-x_i
andC-PK_i
.
At the end, the n
users perform the following:
- Combine all
C-PK_ij
to getC-PK_i
using homomorphic addition. - Verify the proof for
C-PK_i
andPub-x_i
. - Remove the participant
i
if the proof verification fails. - Return the public encryption key as,
PK = prod(Pub-x_i ^ (n! * L_i))
whereL_i
is lagrange coefficient.
To achieve proactive security, we use the distributed key re-sharing technique desribed in https://eprint.iacr.org/2021/339 but modified for class group keys.
- User Group - Collective of individuals who jointly possess cryptocurrency assets.
- Policy - Programmable rules describing conditions that must be fulfilled to generate signatures on each possible message.
- Policy Contract - Policy implemented in the form of a smart contract.
- Validator - Helps in generating signatures upon successful policy compliance check.
- Validator Network - Large network of validators to achieve decentralisation for policy enforcement.
Validator network is setup by performing the DKG process described above. Finally, a public encryption key of the validator network is generated (ek_v
) and every validator has a share of decryption key, such that any threshold (pre-defined) number of validators can come online to evaluate the complete decryption key (although never required).
User Group first defines the policy and the number of different threshold values (|T|
) programmed in the policy. The group then performs class group DKG |T|
number of times with appropriate threshold values (declared in the policy).
After the generation of |T|
public encryption keys, they are combined with the validator network's public key (ek_v
) to get |T|
global encryption keys.
Each user group member then perform the following to generate ECDSA siging key distributively:
- Sample a random number
a_i
less than the order ofSECP256K1
(this will be the additive share of the signing key). - Compute
a_i.G
andenc(a_i, ek)
for allek
in the global encryption keys. - Broadcast
a_i.G
and the encrypted values along with the proofs.
Finally, all the encrypted values (for a particular global encryption key) are added together to get the encrypted signing key.
enc(X, ek) = sum(enc(a_i, ek))
for allek
in global encryption keys (homomorphic addition).
These keys are then broadcasted to the network.
After the setup, the signature over a message can be generated as follows:
- User group creates a signature generation request to the network by providing the message and number of parties available to sign the message.
- The network checks the message and ids of online members against the policy contract for compliance.
- If the compliance check passes, the validator network helps in generating encrypted signature from the encrypted private key and then help in the decryption process.
Note: There are 3 rounds in the signature generation process, and different set of validators can participate in different rounds, making the scheme more robust and high-throughput.
Any t_p
number of users, if they wish to generate a signature on a message M
participate in the following three rounds, after successful policy compliance check, using the encrypted signing key enc(X, ek_tp)
where ek_tp
's corresponding decryption key is distributed such that t_p
number of users + t_v
number of validators from validator network can decrypt the data.
All tp
number of users and any set of t_v
number of validators perform the following:
- Sample a random value
k_i
less than the order ofSECP256K1
curve. - Compute
K_i = k_i.G
whereG
is the generator point and.
represents ECC scalar point multiplication. - Compute
enc(k_i, ek_tp)
. - Broadcast the values
K_i
andenc(k_i, ek_tp)
.
At the end, all K_i
and enc(k_i, ek_tp)
values are combined to get:
K = sum(K_i)
(ECC point addition)r = x-coord(K)
enc(k, ek_tp) = sum(enc(k_i, ek_tp))
(homomorphic addition)
All tp
number of users and any set of t_v
number of validators perform the following:
- Sample a random value
p_i
less than the order ofSECP256K1
curve. - Compute
enc(p_i, ek_tp)
. - Fetch
enc(k, ek_tp)
and computeenc(p_i * k, ek_tp) = p_i * enc(k, ek_tp)
(homomorphic scalar multiplication). - Fetch
enc(X, ek_tp)
and computeenc(p_i * X, ek_tp) = p_i * enc(X, ek_tp)
(homomorphic scalar multiplication). - Broadcast the values
enc(p_i, ek_tp)
,enc(p_i * k, ek_tp)
andenc(p_i * X, ek_tp)
.
At the end, the following computation is performed:
enc(p, ek_tp) = sum(enc(p_i, ek_tp))
(homomorphic addition)enc(p * k, ek_tp) = sum(enc(p_i * k, ek_tp))
(homomorphic addition)enc(p * X, ek_tp) = sum(enc(p_i * X, ek_tp))
(homomorphic addition)enc(z, ek_tp) = (H(M) * enc(p, ek_tp)) + (r * enc(p * X, ek_tp))
(homomorphic addition and scalar multiplication)
All tp
number of users and any set of t_v
number of validators perform the following:
- Compute partial decryption
w_i = partDec(enc(p * k, ek_tp))
. - Compute partial decryption
z_i = partDec(enc(z, ek_tp))
. - Broadcast the values
w_i
andz_i
.
At the end, the following computation is performed:
- Aggregate partial decryptions of
enc(p * k, ek_tp)
to getw = aggPartDecs(w_i)
. - Aggregate partial decryptions of
enc(z, ek_tp)
to getz = aggPartDecs(z_i)
. - Compute
s = z / w
(modulo order ofSECP256K1
curve).
Finally, the r
and s
values are combined together to get the signature.
To compile the code, a C++ compiler and CMake 3.5.1 or later are necessary, and the following libraries are required:
- GMP
- openSSL
On Debian and Ubuntu, the necessary files can be installed with
apt install g++ libgmp-dev libssl-dev cmake
- Clone the repo and the submodules,
git clone https://github.com/Cypherock/cychain-poc.git --recurse-submodules
cd cychain-poc
mkdir build
cd build
cmake ..
make
Run the executable cychain-sig-poc
in build/
directory as follows:
./cychain-sig-poc <num-of-users> <num-of-validators> <sec-level>
Examples:
./cychain-sig-poc 10 1000 128
./cychain-sig-poc 100 1000 128
./cychain-sig-poc 100 2000 128
<sec-level> can only be 128 or 256.
- https://eprint.iacr.org/2015/047
- https://eprint.iacr.org/2018/791
- https://dl.acm.org/doi/abs/10.1007/978-3-030-16458-4_20
- https://eprint.iacr.org/2022/1466
- https://eprint.iacr.org/2022/1143
- https://eprint.iacr.org/2021/205
- https://eprint.iacr.org/2022/1437
- https://www.ndss-symposium.org/ndss-paper/secure-multiparty-computation-of-threshold-signatures-made-more-efficient/
- https://eprint.iacr.org/2021/339