From 0505c22dea9027e4a726bab71b4274e601110a17 Mon Sep 17 00:00:00 2001 From: Paul Staadecker Date: Tue, 28 Jun 2022 09:03:11 -0400 Subject: [PATCH 01/15] Added files --- examples/RSAprot.py | 12 ++++++++++++ zksk/rsa_group.py | 24 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 examples/RSAprot.py create mode 100644 zksk/rsa_group.py diff --git a/examples/RSAprot.py b/examples/RSAprot.py new file mode 100644 index 0000000..89f67f1 --- /dev/null +++ b/examples/RSAprot.py @@ -0,0 +1,12 @@ +from zksk import Secret, DLRep +from zksk import utils +import petlib +from zksk.rsa_group import RSAGroup, IntPt + +n = 35 +x = Secret() +g = IntPt(29, RSAGroup(n)) +y = 3 * g +stmt = DLRep(y, x * g) +proof = stmt.prove({x: 3}) +print(stmt.verify(proof)) diff --git a/zksk/rsa_group.py b/zksk/rsa_group.py new file mode 100644 index 0000000..340d213 --- /dev/null +++ b/zksk/rsa_group.py @@ -0,0 +1,24 @@ +from petlib.bn import Bn + +# Creates a class for RSA group elements which is structured in the same way as petlib.ec.EcPt, to allow users to use RSA groups in their proofs. +class RSAGroup: + def __init__(self, value): + self.value = value + + def order(self): + return Bn(self.value) + + def infinite(self): + return IntPt(1, self) + + +class IntPt: + def __init__(self, value, modulus): + self.pt = value + self.group = modulus + + def __add__(self, o): + return IntPt((self.pt + o.pt) % self.group.value, self.group) + + def __rmul__(self, o): + return IntPt(pow(self.pt, int(o), self.group.value), self.group) From 6ab352b32c537eedbaace39a624234f86bf058bd Mon Sep 17 00:00:00 2001 From: Paul Staadecker Date: Thu, 30 Jun 2022 15:32:44 -0400 Subject: [PATCH 02/15] Changes --- examples/RSAprot.py | 10 ++++++---- zksk/utils/groups.py | 13 +++++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/examples/RSAprot.py b/examples/RSAprot.py index 89f67f1..fc8aa9b 100644 --- a/examples/RSAprot.py +++ b/examples/RSAprot.py @@ -1,11 +1,13 @@ from zksk import Secret, DLRep from zksk import utils -import petlib -from zksk.rsa_group import RSAGroup, IntPt +from petlib.bn import Bn -n = 35 +p = Bn.get_prime(128, safe=1) +q = Bn.get_prime(128, safe=1) +assert p != q +n = p * q x = Secret() -g = IntPt(29, RSAGroup(n)) +g = utils.groups.get_quad_res(n) y = 3 * g stmt = DLRep(y, x * g) proof = stmt.prove({x: 3}) diff --git a/zksk/utils/groups.py b/zksk/utils/groups.py index 4520762..dafe2fd 100644 --- a/zksk/utils/groups.py +++ b/zksk/utils/groups.py @@ -2,6 +2,7 @@ import secrets import hashlib import warnings +import random from petlib.bn import Bn @@ -86,6 +87,18 @@ def get_random_num(bits): return order.random() +def get_quad_res(n): + """ + Draw a random quadratic residue modulo n, coprime to n. Here n is a Bignum + """ + b = n.num_bits() + while True: + q = Bn.from_num(get_random_num(b)) + if q < n and math.gcd(int(q), int(n)) == 1: + break + return (q * q) % n + + def sum_bn_array(arr, modulus): """ Sum an array of big numbers under a modulus. From 48b393a5dac0a76fcc2398cc8cf2982a8a2538f8 Mon Sep 17 00:00:00 2001 From: Paul Staadecker Date: Tue, 19 Jul 2022 14:59:43 -0400 Subject: [PATCH 03/15] Disfunctional --- examples/RSAprot.py | 12 ++++++++++-- zksk/rsa_group.py | 12 ++++++++++-- zksk/utils/groups.py | 5 +++-- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/examples/RSAprot.py b/examples/RSAprot.py index fc8aa9b..dc29dc6 100644 --- a/examples/RSAprot.py +++ b/examples/RSAprot.py @@ -1,5 +1,6 @@ from zksk import Secret, DLRep from zksk import utils +from zksk.rsa_group import RSAGroup from petlib.bn import Bn p = Bn.get_prime(128, safe=1) @@ -10,5 +11,12 @@ g = utils.groups.get_quad_res(n) y = 3 * g stmt = DLRep(y, x * g) -proof = stmt.prove({x: 3}) -print(stmt.verify(proof)) + +prover = stmt.get_prover({x: 3}) +verifier = stmt.get_verifier() + +commitment = prover.commit() +challenge = verifier.send_challenge(commitment) +response = prover.compute_response(challenge) +# proof = stmt.prove({x: 3}) +print(verifier.verify(response)) diff --git a/zksk/rsa_group.py b/zksk/rsa_group.py index 340d213..94a340b 100644 --- a/zksk/rsa_group.py +++ b/zksk/rsa_group.py @@ -2,17 +2,25 @@ # Creates a class for RSA group elements which is structured in the same way as petlib.ec.EcPt, to allow users to use RSA groups in their proofs. class RSAGroup: + # Must take a Bignum as argument def __init__(self, value): self.value = value def order(self): - return Bn(self.value) + return self.value def infinite(self): return IntPt(1, self) + def wsum(self, weights, elems): + res = IntPt(0, self) + for i in range(0, len(elems)): + res = res + (weights[i] * elems[i]) + return res + class IntPt: + # Must take one bignum and one RSAGroup as arguments def __init__(self, value, modulus): self.pt = value self.group = modulus @@ -21,4 +29,4 @@ def __add__(self, o): return IntPt((self.pt + o.pt) % self.group.value, self.group) def __rmul__(self, o): - return IntPt(pow(self.pt, int(o), self.group.value), self.group) + return IntPt(pow(self.pt, o, self.group.value), self.group) diff --git a/zksk/utils/groups.py b/zksk/utils/groups.py index dafe2fd..4ec8936 100644 --- a/zksk/utils/groups.py +++ b/zksk/utils/groups.py @@ -8,6 +8,7 @@ from zksk.consts import DEFAULT_GROUP from zksk.exceptions import InvalidExpression +from zksk.rsa_group import IntPt, RSAGroup def get_random_point(group=None, random_bits=256, seed=None): @@ -89,14 +90,14 @@ def get_random_num(bits): def get_quad_res(n): """ - Draw a random quadratic residue modulo n, coprime to n. Here n is a Bignum + Draw a random quadratic residue modulo n, coprime to n. Here n is a Bignum and the result is a group element """ b = n.num_bits() while True: q = Bn.from_num(get_random_num(b)) if q < n and math.gcd(int(q), int(n)) == 1: break - return (q * q) % n + return IntPt((q * q) % n, RSAGroup(n)) def sum_bn_array(arr, modulus): From fa73d6b01b72aa6a5a4c622332c17bf38e23bcc1 Mon Sep 17 00:00:00 2001 From: Paul Staadecker Date: Tue, 26 Jul 2022 15:56:00 -0400 Subject: [PATCH 04/15] IP working --- examples/RSAprot.py | 11 ++++++----- zksk/primitives/dlrep.py | 3 ++- zksk/rsa_group.py | 19 ++++++++++++++++--- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/examples/RSAprot.py b/examples/RSAprot.py index dc29dc6..37cfabe 100644 --- a/examples/RSAprot.py +++ b/examples/RSAprot.py @@ -1,14 +1,15 @@ from zksk import Secret, DLRep -from zksk import utils -from zksk.rsa_group import RSAGroup -from petlib.bn import Bn +from zksk.utils import groups +from zksk import rsa_group +from zksk.primitives.dlrep import DLRepVerifier +from petlib.bn import Bn, force_Bn p = Bn.get_prime(128, safe=1) q = Bn.get_prime(128, safe=1) assert p != q n = p * q x = Secret() -g = utils.groups.get_quad_res(n) +g = groups.get_quad_res(n) y = 3 * g stmt = DLRep(y, x * g) @@ -18,5 +19,5 @@ commitment = prover.commit() challenge = verifier.send_challenge(commitment) response = prover.compute_response(challenge) -# proof = stmt.prove({x: 3}) + print(verifier.verify(response)) diff --git a/zksk/primitives/dlrep.py b/zksk/primitives/dlrep.py index 27ec139..55696bd 100644 --- a/zksk/primitives/dlrep.py +++ b/zksk/primitives/dlrep.py @@ -13,6 +13,7 @@ """ from hashlib import sha256 +from urllib import response from petlib.bn import Bn @@ -242,7 +243,7 @@ def compute_response(self, challenge): """ order = self.stmt.bases[0].group.order() resps = [ - (self.secret_values[self.stmt.secret_vars[i]] * challenge + k) % order + (self.secret_values[self.stmt.secret_vars[i]] * challenge + k) for i, k in enumerate(self.ks) ] return resps diff --git a/zksk/rsa_group.py b/zksk/rsa_group.py index 94a340b..a093da8 100644 --- a/zksk/rsa_group.py +++ b/zksk/rsa_group.py @@ -1,4 +1,5 @@ from petlib.bn import Bn +from sympy import mod_inverse # Creates a class for RSA group elements which is structured in the same way as petlib.ec.EcPt, to allow users to use RSA groups in their proofs. class RSAGroup: @@ -13,11 +14,14 @@ def infinite(self): return IntPt(1, self) def wsum(self, weights, elems): - res = IntPt(0, self) + res = IntPt(Bn(1), self) for i in range(0, len(elems)): res = res + (weights[i] * elems[i]) return res + def __eq__(self, other): + return self.value == other.value + class IntPt: # Must take one bignum and one RSAGroup as arguments @@ -26,7 +30,16 @@ def __init__(self, value, modulus): self.group = modulus def __add__(self, o): - return IntPt((self.pt + o.pt) % self.group.value, self.group) + return IntPt((self.pt * o.pt) % self.group.value, self.group) def __rmul__(self, o): - return IntPt(pow(self.pt, o, self.group.value), self.group) + if o < 0: + return IntPt( + pow(self.pt.mod_inverse(self.group.value), -o, self.group.value), + self.group, + ) + else: + return IntPt(pow(self.pt, o, self.group.value), self.group) + + def __eq__(self, other): + return (self.pt == other.pt) and (self.group == other.group) From a324ec12837326054b19ca6910c4281cf1f5ecf2 Mon Sep 17 00:00:00 2001 From: Paul Staadecker Date: Tue, 2 Aug 2022 11:35:41 -0400 Subject: [PATCH 05/15] Non-interactive proofs are working --- .pre-commit-config.yaml | 2 +- examples/RSAprot.py | 8 +++++++- tests/test_pairings.py | 18 +++++++++++++++++- zksk/rsa_group.py | 23 +++++++++++++++++++++++ 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0c32adf..1c9b4a5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/psf/black - rev: stable + rev: 22.6.0 hooks: - id: black language_version: python3.6 diff --git a/examples/RSAprot.py b/examples/RSAprot.py index 37cfabe..a6893d7 100644 --- a/examples/RSAprot.py +++ b/examples/RSAprot.py @@ -1,6 +1,6 @@ from zksk import Secret, DLRep from zksk.utils import groups -from zksk import rsa_group +from zksk.rsa_group import RSAGroup, IntPt from zksk.primitives.dlrep import DLRepVerifier from petlib.bn import Bn, force_Bn @@ -16,8 +16,14 @@ prover = stmt.get_prover({x: 3}) verifier = stmt.get_verifier() +# Interactive proof: commitment = prover.commit() challenge = verifier.send_challenge(commitment) response = prover.compute_response(challenge) print(verifier.verify(response)) + +# Non-interactive proof: +proof = stmt.prove({x: 3}) + +print(stmt.verify(proof)) diff --git a/tests/test_pairings.py b/tests/test_pairings.py index c355ba1..efff97b 100644 --- a/tests/test_pairings.py +++ b/tests/test_pairings.py @@ -5,8 +5,10 @@ from bplib.bp import BpGroup from petlib import pack +from petlib.bn import Bn from zksk.pairings import BilinearGroupPair, G1Point, AdditivePoint +from zksk.rsa_group import RSAGroup, IntPt @pytest.fixture @@ -52,7 +54,7 @@ def test_additive_point(bp_group, group_pair): assert AdditivePoint(g ** (g.group.order()), group_pair) == group_pair.GT.infinite() r = bp_group.order().random() - g1, g1mg = g ** r, r * gmg + g1, g1mg = g**r, r * gmg assert g1 == g1mg.pt assert g1 * g1 * g1 == (g1mg + g1mg + g1mg).pt assert g1.export() == g1mg.export() @@ -114,3 +116,17 @@ def test_pack_unpack_gt(group_pair): pt2 = pack.decode(data) assert pt1 == pt2 + + +def test_pack_unpack_rsa_group(): + n = RSAGroup(Bn(15)) + q = IntPt(Bn(4), n) + + enc_n = pack.encode(n) + enc_q = pack.encode(q) + + n2 = pack.decode(enc_n) + q2 = pack.decode(enc_q) + + assert n == n2 + assert q == q2 diff --git a/zksk/rsa_group.py b/zksk/rsa_group.py index a093da8..ebe42ac 100644 --- a/zksk/rsa_group.py +++ b/zksk/rsa_group.py @@ -1,4 +1,6 @@ +from curses.ascii import RS from petlib.bn import Bn +from petlib.pack import * from sympy import mod_inverse # Creates a class for RSA group elements which is structured in the same way as petlib.ec.EcPt, to allow users to use RSA groups in their proofs. @@ -43,3 +45,24 @@ def __rmul__(self, o): def __eq__(self, other): return (self.pt == other.pt) and (self.group == other.group) + + +def enc_RSAGroup(obj): + return encode(obj.value) + + +def dec_RSAGroup(data): + return RSAGroup(decode(data)) + + +def enc_IntPt(obj): + return encode([obj.pt, obj.group]) + + +def dec_IntPt(data): + d = decode(data) + return IntPt(d[0], d[1]) + + +register_coders(RSAGroup, 10, enc_RSAGroup, dec_RSAGroup) +register_coders(IntPt, 11, enc_IntPt, dec_IntPt) From 0972b4ffbeee9043c8cd40a0590c212d5bf73622 Mon Sep 17 00:00:00 2001 From: Paul Staadecker Date: Wed, 3 Aug 2022 13:42:39 -0400 Subject: [PATCH 06/15] change config --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1c9b4a5..0c32adf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/psf/black - rev: 22.6.0 + rev: stable hooks: - id: black language_version: python3.6 From 11f35918dd251a20d64280abdccae13f7a1ff564 Mon Sep 17 00:00:00 2001 From: Paul Staadecker Date: Fri, 5 Aug 2022 12:10:12 -0400 Subject: [PATCH 07/15] small changes to consistency --- examples/RSAprot.py | 29 --------------------------- examples/simple_rsarep.py | 41 +++++++++++++++++++++++++++++++++++++++ tests/test_pairings.py | 16 --------------- zksk/primitives/dlrep.py | 1 - zksk/rsa_group.py | 6 ++++-- zksk/utils/groups.py | 1 + 6 files changed, 46 insertions(+), 48 deletions(-) delete mode 100644 examples/RSAprot.py create mode 100644 examples/simple_rsarep.py diff --git a/examples/RSAprot.py b/examples/RSAprot.py deleted file mode 100644 index a6893d7..0000000 --- a/examples/RSAprot.py +++ /dev/null @@ -1,29 +0,0 @@ -from zksk import Secret, DLRep -from zksk.utils import groups -from zksk.rsa_group import RSAGroup, IntPt -from zksk.primitives.dlrep import DLRepVerifier -from petlib.bn import Bn, force_Bn - -p = Bn.get_prime(128, safe=1) -q = Bn.get_prime(128, safe=1) -assert p != q -n = p * q -x = Secret() -g = groups.get_quad_res(n) -y = 3 * g -stmt = DLRep(y, x * g) - -prover = stmt.get_prover({x: 3}) -verifier = stmt.get_verifier() - -# Interactive proof: -commitment = prover.commit() -challenge = verifier.send_challenge(commitment) -response = prover.compute_response(challenge) - -print(verifier.verify(response)) - -# Non-interactive proof: -proof = stmt.prove({x: 3}) - -print(stmt.verify(proof)) diff --git a/examples/simple_rsarep.py b/examples/simple_rsarep.py new file mode 100644 index 0000000..923593f --- /dev/null +++ b/examples/simple_rsarep.py @@ -0,0 +1,41 @@ +""" +Proof of knowledge of a discrete logarithm in a subgroup of an RSA group: +PK{ (x): y = x * g} +""" + +from petlib.bn import Bn + +from zksk import Secret, DLRep +from zksk.utils import groups + +p = Bn.get_prime(128, safe=1) +q = Bn.get_prime(128, safe=1) +n = p * q + +# Create a generator for a subgroup of the RSA group of order n. +g = groups.get_quad_res(n) + +# Preparing the secret. +# In practice, this should probably be a big integer (petlib.bn.Bn) +x = Secret() + +# Setup the proof statement. + +# First, compute the "left-hand side". +y = 3 * g + +# Next, create the proof statement. +stmt = DLRep(y, x * g) + +# Simulate the prover and the verifier interacting. +prover = stmt.get_prover({x: 3}) +verifier = stmt.get_verifier() + +commitment = prover.commit() +challenge = verifier.send_challenge(commitment) +response = prover.compute_response(challenge) +assert verifier.verify(response) + +# Non-interactive proof. +nizk = stmt.prove({x: 3}) +assert stmt.verify(nizk) diff --git a/tests/test_pairings.py b/tests/test_pairings.py index efff97b..4c92313 100644 --- a/tests/test_pairings.py +++ b/tests/test_pairings.py @@ -5,10 +5,8 @@ from bplib.bp import BpGroup from petlib import pack -from petlib.bn import Bn from zksk.pairings import BilinearGroupPair, G1Point, AdditivePoint -from zksk.rsa_group import RSAGroup, IntPt @pytest.fixture @@ -116,17 +114,3 @@ def test_pack_unpack_gt(group_pair): pt2 = pack.decode(data) assert pt1 == pt2 - - -def test_pack_unpack_rsa_group(): - n = RSAGroup(Bn(15)) - q = IntPt(Bn(4), n) - - enc_n = pack.encode(n) - enc_q = pack.encode(q) - - n2 = pack.decode(enc_n) - q2 = pack.decode(enc_q) - - assert n == n2 - assert q == q2 diff --git a/zksk/primitives/dlrep.py b/zksk/primitives/dlrep.py index 55696bd..b6ba16c 100644 --- a/zksk/primitives/dlrep.py +++ b/zksk/primitives/dlrep.py @@ -13,7 +13,6 @@ """ from hashlib import sha256 -from urllib import response from petlib.bn import Bn diff --git a/zksk/rsa_group.py b/zksk/rsa_group.py index ebe42ac..35bf867 100644 --- a/zksk/rsa_group.py +++ b/zksk/rsa_group.py @@ -1,7 +1,9 @@ -from curses.ascii import RS +""" +Allow zero knowledge proofs in subgroups of RSA groups (groups of integers modulo the product of two safe primes), instead of only in groups of prime order. +""" + from petlib.bn import Bn from petlib.pack import * -from sympy import mod_inverse # Creates a class for RSA group elements which is structured in the same way as petlib.ec.EcPt, to allow users to use RSA groups in their proofs. class RSAGroup: diff --git a/zksk/utils/groups.py b/zksk/utils/groups.py index 4ec8936..ff96ea5 100644 --- a/zksk/utils/groups.py +++ b/zksk/utils/groups.py @@ -1,3 +1,4 @@ +from ast import IsNot import math import secrets import hashlib From 44163891de8adfb11daf88a3038c33e6c1048029 Mon Sep 17 00:00:00 2001 From: Paul Staadecker Date: Fri, 5 Aug 2022 18:35:23 -0400 Subject: [PATCH 08/15] No more RSAGroup.order() --- zksk/primitives/dlrep.py | 34 ++++++++++++++++++++++++++-------- zksk/rsa_group.py | 3 --- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/zksk/primitives/dlrep.py b/zksk/primitives/dlrep.py index b6ba16c..4b5588b 100644 --- a/zksk/primitives/dlrep.py +++ b/zksk/primitives/dlrep.py @@ -13,11 +13,14 @@ """ from hashlib import sha256 +from black import out from petlib.bn import Bn +from petlib.ec import EcGroup from zksk.base import Verifier, Prover, SimulationTranscript from zksk.expr import Secret, Expression +from zksk.rsa_group import RSAGroup from zksk.utils import get_random_num from zksk.consts import CHALLENGE_LENGTH from zksk.composition import ComposableProofStmt @@ -164,9 +167,17 @@ def get_randomizers(self): of the proof. """ output = {} - order = self.bases[0].group.order() - for sec in set(self.secret_vars): - output.update({sec: order.random()}) + if isinstance(self.bases[0].group, EcGroup): + order = self.bases[0].group.order() + for sec in set(self.secret_vars): + output.update({sec: order.random()}) + if isinstance(self.bases[0].group, RSAGroup): + rand_range = self.bases[0].group.value * pow(2, 2 * CHALLENGE_LENGTH) + for sec in set(self.secret_vars): + val = rand_range.random() + if Bn(2).random() == 0: + val = -val + output.update({sec: val}) return output def recompute_commitment(self, challenge, responses): @@ -240,9 +251,16 @@ def compute_response(self, challenge): Returns: A list of responses """ - order = self.stmt.bases[0].group.order() - resps = [ - (self.secret_values[self.stmt.secret_vars[i]] * challenge + k) - for i, k in enumerate(self.ks) - ] + resps = [] + if isinstance(self.stmt.bases[0].group, EcGroup): + order = self.stmt.bases[0].group.order() + resps = [ + (self.secret_values[self.stmt.secret_vars[i]] * challenge + k) % order + for i, k in enumerate(self.ks) + ] + if isinstance(self.stmt.bases[0].group, RSAGroup): + resps = [ + (self.secret_values[self.stmt.secret_vars[i]] * challenge + k) + for i, k in enumerate(self.ks) + ] return resps diff --git a/zksk/rsa_group.py b/zksk/rsa_group.py index 35bf867..da3ef7f 100644 --- a/zksk/rsa_group.py +++ b/zksk/rsa_group.py @@ -11,9 +11,6 @@ class RSAGroup: def __init__(self, value): self.value = value - def order(self): - return self.value - def infinite(self): return IntPt(1, self) From c615b4f55cb8c6de422f3f20dd054781e3d93629 Mon Sep 17 00:00:00 2001 From: Paul Staadecker Date: Fri, 5 Aug 2022 19:05:13 -0400 Subject: [PATCH 09/15] added test --- tests/test_rsagroup.py | 27 +++++++++++++++++++++++++++ zksk/primitives/dlrep.py | 20 +++++++++++--------- 2 files changed, 38 insertions(+), 9 deletions(-) create mode 100644 tests/test_rsagroup.py diff --git a/tests/test_rsagroup.py b/tests/test_rsagroup.py new file mode 100644 index 0000000..5f59beb --- /dev/null +++ b/tests/test_rsagroup.py @@ -0,0 +1,27 @@ +import pytest + +from petlib.bn import Bn + +from zksk import Secret +from zksk.primitives.dlrep import DLRep, DLRepProver +from zksk.utils.debug import SigmaProtocol +from zksk.utils.groups import get_quad_res + + +def test_rsagroup_interactive_1(): + p = Bn.get_prime(256, safe=1) + q = Bn.get_prime(256, safe=1) + n = p * q + + g = get_quad_res(n) + h = ((p - 1) * (q - 1)).random() * g + sk1, sk2 = n.random(), n.random() + pk = sk1 * g + sk2 * h + + x1 = Secret() + x2 = Secret() + p = DLRep(pk, x1 * g + x2 * h) + prover = p.get_prover({x1: sk1, x2: sk2}) + verifier = p.get_verifier() + protocol = SigmaProtocol(verifier, prover) + assert protocol.verify() diff --git a/zksk/primitives/dlrep.py b/zksk/primitives/dlrep.py index 4b5588b..b2ae07e 100644 --- a/zksk/primitives/dlrep.py +++ b/zksk/primitives/dlrep.py @@ -167,10 +167,6 @@ def get_randomizers(self): of the proof. """ output = {} - if isinstance(self.bases[0].group, EcGroup): - order = self.bases[0].group.order() - for sec in set(self.secret_vars): - output.update({sec: order.random()}) if isinstance(self.bases[0].group, RSAGroup): rand_range = self.bases[0].group.value * pow(2, 2 * CHALLENGE_LENGTH) for sec in set(self.secret_vars): @@ -178,6 +174,11 @@ def get_randomizers(self): if Bn(2).random() == 0: val = -val output.update({sec: val}) + else: + order = self.bases[0].group.order() + for sec in set(self.secret_vars): + output.update({sec: order.random()}) + return output def recompute_commitment(self, challenge, responses): @@ -252,15 +253,16 @@ def compute_response(self, challenge): A list of responses """ resps = [] - if isinstance(self.stmt.bases[0].group, EcGroup): - order = self.stmt.bases[0].group.order() + if isinstance(self.stmt.bases[0].group, RSAGroup): resps = [ - (self.secret_values[self.stmt.secret_vars[i]] * challenge + k) % order + (self.secret_values[self.stmt.secret_vars[i]] * challenge + k) for i, k in enumerate(self.ks) ] - if isinstance(self.stmt.bases[0].group, RSAGroup): + else: + order = self.stmt.bases[0].group.order() resps = [ - (self.secret_values[self.stmt.secret_vars[i]] * challenge + k) + (self.secret_values[self.stmt.secret_vars[i]] * challenge + k) % order for i, k in enumerate(self.ks) ] + return resps From 1979936b835fa15d16233dd32709ec04e5e7a3ed Mon Sep 17 00:00:00 2001 From: Paul Staadecker Date: Thu, 11 Aug 2022 13:42:38 -0400 Subject: [PATCH 10/15] Added trusted setup for RSA DLRep --- examples/simple_rsarep.py | 10 +++----- tests/test_rsagroup.py | 10 +++----- zksk/primitives/dlrep.py | 2 +- zksk/rsa_group.py | 48 ++++++++++++++++++++++++++++++++------- zksk/utils/groups.py | 13 ----------- 5 files changed, 47 insertions(+), 36 deletions(-) diff --git a/examples/simple_rsarep.py b/examples/simple_rsarep.py index 923593f..f88eb86 100644 --- a/examples/simple_rsarep.py +++ b/examples/simple_rsarep.py @@ -6,14 +6,10 @@ from petlib.bn import Bn from zksk import Secret, DLRep -from zksk.utils import groups +from zksk.rsa_group import rsa_dlrep_trusted_setup -p = Bn.get_prime(128, safe=1) -q = Bn.get_prime(128, safe=1) -n = p * q - -# Create a generator for a subgroup of the RSA group of order n. -g = groups.get_quad_res(n) +# Create a generator for a subgroup of an RSA group. +[g] = rsa_dlrep_trusted_setup(bits=1024, num=1) # Preparing the secret. # In practice, this should probably be a big integer (petlib.bn.Bn) diff --git a/tests/test_rsagroup.py b/tests/test_rsagroup.py index 5f59beb..33ecf2b 100644 --- a/tests/test_rsagroup.py +++ b/tests/test_rsagroup.py @@ -5,16 +5,12 @@ from zksk import Secret from zksk.primitives.dlrep import DLRep, DLRepProver from zksk.utils.debug import SigmaProtocol -from zksk.utils.groups import get_quad_res +from zksk.rsa_group import rsa_dlrep_trusted_setup def test_rsagroup_interactive_1(): - p = Bn.get_prime(256, safe=1) - q = Bn.get_prime(256, safe=1) - n = p * q - - g = get_quad_res(n) - h = ((p - 1) * (q - 1)).random() * g + [g, h] = rsa_dlrep_trusted_setup(bits=1024, num=2) + n = g.group.modulus sk1, sk2 = n.random(), n.random() pk = sk1 * g + sk2 * h diff --git a/zksk/primitives/dlrep.py b/zksk/primitives/dlrep.py index b2ae07e..747821c 100644 --- a/zksk/primitives/dlrep.py +++ b/zksk/primitives/dlrep.py @@ -168,7 +168,7 @@ def get_randomizers(self): """ output = {} if isinstance(self.bases[0].group, RSAGroup): - rand_range = self.bases[0].group.value * pow(2, 2 * CHALLENGE_LENGTH) + rand_range = self.bases[0].group.modulus * pow(2, 2 * CHALLENGE_LENGTH) for sec in set(self.secret_vars): val = rand_range.random() if Bn(2).random() == 0: diff --git a/zksk/rsa_group.py b/zksk/rsa_group.py index da3ef7f..c676254 100644 --- a/zksk/rsa_group.py +++ b/zksk/rsa_group.py @@ -1,15 +1,46 @@ """ Allow zero knowledge proofs in subgroups of RSA groups (groups of integers modulo the product of two safe primes), instead of only in groups of prime order. + +Example: +PK{(alpha): y = alpha * g} +where y, and g are elements of a subgroup of an RSA group of order p * q, for two safe primes p and q. We assume there is a trusted setup which keeps p and q secret from both the Prover and the Verifier, which sends y, g, and p * q to both parties, and which sends alpha to the Prover. + +The protocol we follow for proofs of discrete logarithm representations is inspired from page 34 of Boneh, Bünz and Fisch, Batching Techniques for Accumulators with Applications to IOPs and Stateless Blockchains, Crypto 2019. """ +# To do: Allow ZKPs of other cryptographic primitives in RSA groups. +import math from petlib.bn import Bn from petlib.pack import * -# Creates a class for RSA group elements which is structured in the same way as petlib.ec.EcPt, to allow users to use RSA groups in their proofs. +# This sets up the RSA group and the subgroup generators +# Example: +# [g,h] = rsa_dlrep_trusted_setup(bits=1024,num = 2) +# g and h are two generators of the subgroup of quadratic residues of an RSA group of order the product of two 1024 bit primes. +def rsa_dlrep_trusted_setup(bits=1024, num=1): + p = Bn.get_prime(bits, safe=1) + q = Bn.get_prime(bits, safe=1) + n = p * q + b = n.num_bits() + while True: + q = Bn.from_num(Bn(2).pow(bits).random()) + if q < n and math.gcd(int(q), int(n)) == 1: + break + g = IntPt((q * q) % n, RSAGroup(n)) + res = [g] + + num -= 1 + while num != 0: + res.append(((p - 1) * (q - 1)).random() * g) + num -= 1 + return res + + +# This class mimics petlib.ec.EcGroup, but for RSA groups. class RSAGroup: # Must take a Bignum as argument - def __init__(self, value): - self.value = value + def __init__(self, modulus): + self.modulus = modulus def infinite(self): return IntPt(1, self) @@ -21,9 +52,10 @@ def wsum(self, weights, elems): return res def __eq__(self, other): - return self.value == other.value + return self.modulus == other.modulus +# This class mimics petlib.ec.EcPt, but for elements of RSA groups. class IntPt: # Must take one bignum and one RSAGroup as arguments def __init__(self, value, modulus): @@ -31,23 +63,23 @@ def __init__(self, value, modulus): self.group = modulus def __add__(self, o): - return IntPt((self.pt * o.pt) % self.group.value, self.group) + return IntPt((self.pt * o.pt) % self.group.modulus, self.group) def __rmul__(self, o): if o < 0: return IntPt( - pow(self.pt.mod_inverse(self.group.value), -o, self.group.value), + pow(self.pt.mod_inverse(self.group.modulus), -o, self.group.modulus), self.group, ) else: - return IntPt(pow(self.pt, o, self.group.value), self.group) + return IntPt(pow(self.pt, o, self.group.modulus), self.group) def __eq__(self, other): return (self.pt == other.pt) and (self.group == other.group) def enc_RSAGroup(obj): - return encode(obj.value) + return encode(obj.modulus) def dec_RSAGroup(data): diff --git a/zksk/utils/groups.py b/zksk/utils/groups.py index ff96ea5..6b522c1 100644 --- a/zksk/utils/groups.py +++ b/zksk/utils/groups.py @@ -9,7 +9,6 @@ from zksk.consts import DEFAULT_GROUP from zksk.exceptions import InvalidExpression -from zksk.rsa_group import IntPt, RSAGroup def get_random_point(group=None, random_bits=256, seed=None): @@ -89,18 +88,6 @@ def get_random_num(bits): return order.random() -def get_quad_res(n): - """ - Draw a random quadratic residue modulo n, coprime to n. Here n is a Bignum and the result is a group element - """ - b = n.num_bits() - while True: - q = Bn.from_num(get_random_num(b)) - if q < n and math.gcd(int(q), int(n)) == 1: - break - return IntPt((q * q) % n, RSAGroup(n)) - - def sum_bn_array(arr, modulus): """ Sum an array of big numbers under a modulus. From a70645cc7e1fced5c279cd1cdf8c40fc8d4798d7 Mon Sep 17 00:00:00 2001 From: Paul Staadecker Date: Thu, 11 Aug 2022 13:46:12 -0400 Subject: [PATCH 11/15] Removed spurious diffs --- zksk/utils/groups.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/zksk/utils/groups.py b/zksk/utils/groups.py index 6b522c1..4520762 100644 --- a/zksk/utils/groups.py +++ b/zksk/utils/groups.py @@ -1,9 +1,7 @@ -from ast import IsNot import math import secrets import hashlib import warnings -import random from petlib.bn import Bn From d02b1bddae75cfcfaaec0d2298388ba3b5dc4859 Mon Sep 17 00:00:00 2001 From: Paul Staadecker Date: Thu, 11 Aug 2022 13:53:35 -0400 Subject: [PATCH 12/15] Commenting --- zksk/rsa_group.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/zksk/rsa_group.py b/zksk/rsa_group.py index c676254..d8f57a0 100644 --- a/zksk/rsa_group.py +++ b/zksk/rsa_group.py @@ -4,6 +4,7 @@ Example: PK{(alpha): y = alpha * g} where y, and g are elements of a subgroup of an RSA group of order p * q, for two safe primes p and q. We assume there is a trusted setup which keeps p and q secret from both the Prover and the Verifier, which sends y, g, and p * q to both parties, and which sends alpha to the Prover. +We use additive notation for the group operation, so alpha * g is g raised to the exponent alpha. The protocol we follow for proofs of discrete logarithm representations is inspired from page 34 of Boneh, Bünz and Fisch, Batching Techniques for Accumulators with Applications to IOPs and Stateless Blockchains, Crypto 2019. """ @@ -62,9 +63,11 @@ def __init__(self, value, modulus): self.pt = value self.group = modulus + # We use additive notation for the group operation, so IntPt.__add__ is actually multiplication. def __add__(self, o): return IntPt((self.pt * o.pt) % self.group.modulus, self.group) + # Similarly, IntPt.__rmul__ is actually exponentiation. def __rmul__(self, o): if o < 0: return IntPt( From 1cd7e3fff1870c44add04c2474b875252174c125 Mon Sep 17 00:00:00 2001 From: Paul Staadecker Date: Thu, 11 Aug 2022 13:55:50 -0400 Subject: [PATCH 13/15] Commenting the group operation --- examples/simple_rsarep.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/simple_rsarep.py b/examples/simple_rsarep.py index f88eb86..f7bd996 100644 --- a/examples/simple_rsarep.py +++ b/examples/simple_rsarep.py @@ -1,6 +1,8 @@ """ Proof of knowledge of a discrete logarithm in a subgroup of an RSA group: -PK{ (x): y = x * g} +PK{ (x): y = x * g}. + +Here, the group operation is written additively, so x * g is g multiplied by itself x times. """ from petlib.bn import Bn From cb4fde337ee17bad28e8053f305f3e839e38da13 Mon Sep 17 00:00:00 2001 From: Paul Staadecker Date: Thu, 18 Aug 2022 11:47:12 -0400 Subject: [PATCH 14/15] added AND primitive for rsa groups --- tests/test_rsagroup.py | 16 ++++++++++++++++ zksk/composition.py | 40 ++++++++++++++++++++++++++++++---------- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/tests/test_rsagroup.py b/tests/test_rsagroup.py index 33ecf2b..f8b4073 100644 --- a/tests/test_rsagroup.py +++ b/tests/test_rsagroup.py @@ -21,3 +21,19 @@ def test_rsagroup_interactive_1(): verifier = p.get_verifier() protocol = SigmaProtocol(verifier, prover) assert protocol.verify() + + +def test_rsagroup_and_interactive_1(): + [g, h] = rsa_dlrep_trusted_setup(bits=1024, num=2) + n = g.group.modulus + sk1, sk2 = n.random(), n.random() + pk1 = sk1 * g + pk2 = sk2 * h + + x1 = Secret() + x2 = Secret() + p = DLRep(pk1, x1 * g) & DLRep(pk2, x2 * h) + prover = p.get_prover({x1: sk1, x2: sk2}) + verifier = p.get_verifier() + protocol = SigmaProtocol(verifier, prover) + assert protocol.verify() diff --git a/zksk/composition.py b/zksk/composition.py index 0d4ae5b..ec6a39f 100644 --- a/zksk/composition.py +++ b/zksk/composition.py @@ -14,6 +14,7 @@ from zksk.consts import CHALLENGE_LENGTH from zksk.base import Prover, Verifier, SimulationTranscript from zksk.expr import Secret, update_secret_values +from zksk.rsa_group import IntPt from zksk.utils import get_random_num, sum_bn_array from zksk.utils.misc import get_default_attr from zksk.exceptions import StatementSpecError, StatementMismatch @@ -372,14 +373,22 @@ def validate_group_orders(self): # the same group for (word, gen_idx) in mydict.items(): # Word is the key, gen_idx is the value = a list of indices - ref_order = bases[gen_idx[0]].group.order() - - for index in gen_idx: - if bases[index].group.order() != ref_order: - raise GroupMismatchError( - "A shared secret has bases which yield different group orders: %s" - % word - ) + if isinstance(bases[0], IntPt): + ref_modulus = bases[gen_idx[0]].group.modulus + for index in gen_idx: + if bases[index].group.modulus != ref_modulus: + raise GroupMismatchError( + "A shared secret has bases which yield different group orders: %s" + % word + ) + else: + ref_order = bases[gen_idx[0]].group.order() + for index in gen_idx: + if bases[index].group.order() != ref_order: + raise GroupMismatchError( + "A shared secret has bases which yield different group orders: %s" + % word + ) def get_proof_id(self, secret_id_map=None): secret_vars = self.get_secret_vars() @@ -770,8 +779,19 @@ def get_randomizers(self): dict_name_gen = {s: g for s, g in zip(self.get_secret_vars(), self.get_bases())} # Pair each Secret to a randomizer. - for u in dict_name_gen: - random_vals[u] = dict_name_gen[u].group.order().random() + + if isinstance(self.get_bases()[0], IntPt): + rand_range = self.get_bases()[0].group.modulus * pow( + 2, 2 * CHALLENGE_LENGTH + ) + for u in dict_name_gen: + val = rand_range.random() + if Bn(2).random() == 0: + val = -val + random_vals[u] = val + else: + for u in dict_name_gen: + random_vals[u] = dict_name_gen[u].group.order().random() return random_vals From 2a590d7f0e280c42a3c2031dea3ab471fad211d7 Mon Sep 17 00:00:00 2001 From: Paul Staadecker Date: Thu, 18 Aug 2022 12:03:27 -0400 Subject: [PATCH 15/15] added or proof --- tests/test_rsagroup.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/tests/test_rsagroup.py b/tests/test_rsagroup.py index f8b4073..5c5702c 100644 --- a/tests/test_rsagroup.py +++ b/tests/test_rsagroup.py @@ -3,9 +3,9 @@ from petlib.bn import Bn from zksk import Secret -from zksk.primitives.dlrep import DLRep, DLRepProver +from zksk.primitives.dlrep import DLRep from zksk.utils.debug import SigmaProtocol -from zksk.rsa_group import rsa_dlrep_trusted_setup +from zksk.rsa_group import RSAGroup, IntPt, rsa_dlrep_trusted_setup def test_rsagroup_interactive_1(): @@ -37,3 +37,20 @@ def test_rsagroup_and_interactive_1(): verifier = p.get_verifier() protocol = SigmaProtocol(verifier, prover) assert protocol.verify() + + +def test_rsagroup_or_interactive_1(): + [g, h] = rsa_dlrep_trusted_setup(bits=1024, num=2) + n = g.group.modulus + sk1, sk2 = n.random(), n.random() + pk1 = sk1 * g + pk2 = IntPt(Bn(1), RSAGroup(n)) + + x1 = Secret() + x2 = Secret() + p = DLRep(pk1, x1 * g) | DLRep(pk2, x2 * h) + p.subproofs[1].set_simulated() + prover = p.get_prover({x1: sk1, x2: sk2}) + verifier = p.get_verifier() + protocol = SigmaProtocol(verifier, prover) + assert protocol.verify()