def verify(self): """ This implementation of verify has several steps. First, it reorganizes the pubkeys and messages into groups, where each group corresponds to a message. Then, it checks if the siganture has info on how it was aggregated. If so, we exponentiate each pk based on the exponent in the AggregationInfo. If not, we find public keys that share messages with others, and aggregate all of these securely (with exponents.). Finally, since each public key now corresponds to a unique message (since we grouped them), we can verify using the distinct verification procedure. """ message_hashes = self.aggregation_info.message_hashes public_keys = self.aggregation_info.public_keys assert (len(message_hashes) == len(public_keys)) hash_to_public_keys = {} for i in range(len(message_hashes)): if message_hashes[i] in hash_to_public_keys: hash_to_public_keys[message_hashes[i]].append(public_keys[i]) else: hash_to_public_keys[message_hashes[i]] = [public_keys[i]] final_message_hashes = [] final_public_keys = [] ec = public_keys[0].value.ec for message_hash, mapped_keys in hash_to_public_keys.items(): dedup = list(set(mapped_keys)) public_key_sum = JacobianPoint(Fq.one(ec.q), Fq.one(ec.q), Fq.zero(ec.q), True, ec) for public_key in dedup: try: exponent = self.aggregation_info.tree[(message_hash, public_key)] public_key_sum += (public_key.value * exponent) except KeyError: return False final_message_hashes.append(message_hash) final_public_keys.append(public_key_sum.to_affine()) mapped_hashes = [ hash_to_point_prehashed_Fq2(mh) for mh in final_message_hashes ] g1 = Fq(default_ec.n, -1) * generator_Fq() Ps = [g1] + final_public_keys Qs = [self.value.to_affine()] + mapped_hashes res = ate_pairing_multi(Ps, Qs, default_ec) return res == Fq12.one(default_ec.q)
def aggregate(public_keys, secure): """ Aggregates public keys together """ if len(public_keys) < 1: raise Exception("Invalid number of keys") public_keys.sort() computed_Ts = BLS.hash_pks(len(public_keys), public_keys) ec = public_keys[0].value.ec sum_keys = JacobianPoint(Fq.one(ec.q), Fq.one(ec.q), Fq.zero(ec.q), True, ec) for i in range(len(public_keys)): addend = public_keys[i].value if secure: addend *= computed_Ts[i] sum_keys += addend return PublicKey.from_g1(sum_keys)
# vim: syntax=python # # point serialization / deserialization # using the "enhanced ZCash" format proposed in # https://github.com/pairingwg/bls_standard/issues/16 # (C) 2019 Riad S. Wahby <*****@*****.**> # # see the comment at the top of ../sage-impl/serdes.sage for more information import struct from consts import p from curve_ops import from_jacobian, point_eq from fields import Fq, Fq2, sgn0, sqrt_F2 F1_one = Fq.one(p) F1_zero = Fq.zero(p) F2_one = Fq2.one(p) F2_zero = Fq2.zero(p) class DeserError(Exception): pass class SerError(Exception): pass def serialize(P, compressed=True): if isinstance(P[0], Fq):
import sys from consts import p, q from fields import Fq, Fq2 if sys.version_info[0] < 3: sys.exit("This script requires Python3 or PyPy3") ### ## generators for BLS signatures ### # I'd rather have these in consts, but then we'd get an import cycle, consts <-> fields g1gen = (Fq(p, 0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb), Fq(p, 0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1), Fq.one(p)) g2gen = (Fq2(p, 0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8, 0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e), Fq2(p, 0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801, 0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be), Fq2.one(p)) ### ## Basic curve operations ### # Jacobian coordinates def from_jacobian(P): z3inv = ~(P[2] ** 3) return (P[0] * P[2] * z3inv, P[1] * z3inv) # point equality or co-z repr
if sys.version_info[0] < 3: sys.exit("This script requires Python3 or PyPy3") ### ## generators for BLS signatures ### # I'd rather have these in consts, but then we'd get an import cycle, consts <-> fields g1gen = ( Fq( p, 0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb ), Fq( p, 0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1 ), Fq.one(p)) g2gen = ( Fq2( p, 0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8, 0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e ), Fq2( p, 0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801, 0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be ), Fq2.one(p)) ### ## Basic curve operations