def __call__(self, com, g, h, a, b, x, r): """ Get a conjunction of two range-power-of-two proofs. Args: com: Value of the Pedersen commitment, :math:`C = x G + r H` g: First commitment base point :math:`G` h: Second commitment base point :math:`H` a: Lower limit :math:`a` b: Upper limit :math:`b` x: Value for which we construct a range proof r: Randomizer of the commitment :math:`r` """ a = ensure_bn(a) b = ensure_bn(b) num_bits = (b - a - 1).num_bits() offset = Bn(2)**num_bits - (b - a) com_shifted1 = com - a * g com_shifted2 = com_shifted1 + offset * g x1 = Secret() x2 = Secret() if x.value is not None: x1.value = x.value - a x2.value = x.value - a + offset # Ensure secret is in range if x.value < a or x.value >= b: warnings.warn("Secret outside of given range [{}, {})".format( a, b)) com_stmt = DLRep(com, x * g + r * h) p1 = PowerTwoRangeStmt( com=com_shifted1, g=g, h=h, num_bits=num_bits, x=x1, randomizer=r, ) p2 = PowerTwoRangeStmt( com=com_shifted2, g=g, h=h, num_bits=num_bits, x=x2, randomizer=r, ) return com_stmt & p1 & p2
def precommit(self): """ Commit to the bit-decomposition of the value. """ actual_value = ensure_bn(self.x.value) value_as_bits = decompose_into_n_bits(actual_value, self.num_bits) # Set true value to computed secrets for rand in self.randomizers: rand.value = self.order.random() precommitment = {} precommitment["Cs"] = [ b * self.g + r.value * self.h for b, r in zip(value_as_bits, self.randomizers) ] # Compute revealed randomizer rand = Bn(0) power = Bn(1) for r in self.randomizers: rand = rand.mod_add(r.value * power, self.order) power *= 2 rand = rand.mod_sub(self.randomizer.value, self.order) precommitment["rand"] = rand return precommitment
def construct_stmt(self, precommitment): """ Construct the internal proof statement. """ if self.is_prover: # Indicators that tell us which or-clause is true actual_value = ensure_bn(self.x.value) value_as_bits = decompose_into_n_bits(actual_value, self.num_bits) zero_simulated = [b == 1 for b in value_as_bits] one_simulated = [b == 0 for b in value_as_bits] bit_proofs = [] for i in range(self.num_bits): p0 = DLRep(precommitment["Cs"][i], self.randomizers[i] * self.h) p1 = DLRep(precommitment["Cs"][i] - self.g, self.randomizers[i] * self.h) # When we are a prover, mark which disjunct is true if self.is_prover: p0.set_simulated(zero_simulated[i]) p1.set_simulated(one_simulated[i]) bit_proofs.append(p0 | p1) return AndProofStmt(*bit_proofs)
def __call__(self, a, b, x=None): """ Get a conjunction of two range-power-of-two proofs. Args: a: Lower limit :math:`a` b: Upper limit :math:`b` x: Value for which we construct a range proof """ group = EcGroup() g = group.hash_to_point(b"g") h = group.hash_to_point(b"h") r = Secret(value=group.order().random()) com = (x * g + r * h).eval() a = ensure_bn(a) b = ensure_bn(b) num_bits = (b - a - 1).num_bits() offset = Bn(2)**num_bits - (b - a) com_shifted1 = com - a * g com_shifted2 = com_shifted1 + offset * g x1 = Secret() x2 = Secret() if x is not None: x1.value = x.value - a x2.value = x.value - a + offset com_stmt = DLRep(com, x * g + r * h) p1 = PowerTwoRangeStmt( com=com_shifted1, g=g, h=h, num_bits=num_bits, x=x1, randomizer=r, ) p2 = PowerTwoRangeStmt( com=com_shifted2, g=g, h=h, num_bits=num_bits, x=x2, randomizer=r, ) return com_stmt & p1 & p2
def __init__(self, com, g, h, num_bits, x=None, randomizer=None): if not x.value is None and not randomizer.value is None: self.x = x self.randomizer = randomizer self.is_prover = True # Ensure secret is in range self.x.value = ensure_bn(self.x.value) if self.x.value < 0: warnings.warn("Secret is negative") if self.x.value.num_bits() > num_bits: warnings.warn("Secret has more than {} bits".format(num_bits)) else: self.is_prover = False # TODO: Should we combine com with the inner proof? self.com = com self.g = g self.h = h self.order = g.group.order() self.num_bits = num_bits # The constructed proofs need extra randomizers as secrets self.randomizers = [Secret() for _ in range(self.num_bits)]