def test_vectors(): sk1 = PrivateKey.from_seed(bytes([1, 2, 3, 4, 5])) pk1 = sk1.get_public_key() sig1 = sk1.sign(bytes([7, 8, 9])) sk2 = PrivateKey.from_seed(bytes([1, 2, 3, 4, 5, 6])) pk2 = sk2.get_public_key() sig2 = sk2.sign(bytes([7, 8, 9])) assert (sk1.serialize() == bytes.fromhex( "022fb42c08c12de3a6af053880199806532e79515f94e83461612101f9412f9e")) assert (pk1.get_fingerprint() == 0x26d53247) assert (pk2.get_fingerprint() == 0x289bb56e) assert (sig1.serialize() == bytes.fromhex( "93eb2e1cb5efcfb31f2c08b235e8203a67265bc6a13d9f0ab77727293b74a357ff0459ac210dc851fcb8a60cb7d393a419915cfcf83908ddbeac32039aaa3e8fea82efcb3ba4f740f20c76df5e97109b57370ae32d9b70d256a98942e5806065" )) assert (sig2.serialize() == bytes.fromhex( "975b5daa64b915be19b5ac6d47bc1c2fc832d2fb8ca3e95c4805d8216f95cf2bdbb36cc23645f52040e381550727db420b523b57d494959e0e8c0c6060c46cf173872897f14d43b2ac2aec52fc7b46c02c5699ff7a10beba24d3ced4e89c821e" )) agg_sig = Signature.aggregate([sig1, sig2]) agg_pk = PublicKey.aggregate([pk1, pk2], True) agg_sk = PrivateKey.aggregate([sk1, sk2], [pk1, pk2], True) assert (agg_sig.serialize() == bytes.fromhex( "0a638495c1403b25be391ed44c0ab013390026b5892c796a85ede46310ff7d0e0671f86ebe0e8f56bee80f28eb6d999c0a418c5fc52debac8fc338784cd32b76338d629dc2b4045a5833a357809795ef55ee3e9bee532edfc1d9c443bf5bc658" )) assert (agg_sk.sign(bytes([7, 8, 9])).serialize() == agg_sig.serialize()) assert (sig1.verify()) assert (agg_sig.verify()) agg_sig.set_aggregation_info( AggregationInfo.from_msg(agg_pk, bytes([7, 8, 9]))) assert (agg_sig.verify()) sig1.set_aggregation_info(sig2.aggregation_info) assert (not sig1.verify()) sig3 = sk1.sign(bytes([1, 2, 3])) sig4 = sk1.sign(bytes([1, 2, 3, 4])) sig5 = sk2.sign(bytes([1, 2])) agg_sig2 = Signature.aggregate([sig3, sig4, sig5]) assert (agg_sig2.verify()) assert (agg_sig2.serialize() == bytes.fromhex( "8b11daf73cd05f2fe27809b74a7b4c65b1bb79cc1066bdf839d96b97e073c1a635d2ec048e0801b4a208118fdbbb63a516bab8755cc8d850862eeaa099540cd83621ff9db97b4ada857ef54c50715486217bd2ecb4517e05ab49380c041e159b" ))
def test2(): seed = bytes([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) seed2 = bytes([1, 20, 102, 229, 1, 157]) sk = PrivateKey.from_seed(seed) sk_cp = PrivateKey.from_seed(seed) sk2 = PrivateKey.from_seed(seed2) pk = sk.get_public_key() pk2 = sk2.get_public_key() assert (sk == sk_cp) assert (sk != sk2) assert (pk.get_fingerprint() == 0xddad59bb) sk2_ser = sk2.serialize() pk2_ser = pk2.serialize() pk2_copy = PublicKey.from_bytes(pk2_ser) assert (pk2 == pk2_copy) assert (pk != pk2) assert (len(pk2_ser) == 48) assert (len(sk2_ser) == 32) message = bytes("this is the message", "utf-8") sig = sk.sign(message) sig_ser = sig.serialize() sig_cp = Signature.from_bytes(sig_ser) a1 = AggregationInfo.from_msg(pk, message) sig_cp.set_aggregation_info(a1) a2 = sig_cp.get_aggregation_info() assert (a1 == a2) sig2 = sk2.sign(message) assert (len(sig_ser) == 96) assert (sig != sig2) assert (sig == sig_cp) sig_agg = Signature.aggregate([sig, sig2]) result = sig_cp.verify() result2 = sig2.verify() result3 = sig_agg.verify() assert (result) assert (result2) assert (result3) sk2 = sk
def sign_prehashed(self, h): r = hash_to_point_prehashed_Fq2(h).to_jacobian() aggregation_info = AggregationInfo.from_msg_hash( self.get_public_key(), h) return Signature.from_g2(self.value * r, aggregation_info)
def sign(self, m): r = hash_to_point_Fq2(m).to_jacobian() aggregation_info = AggregationInfo.from_msg(self.get_public_key(), m) return Signature.from_g2(self.value * r, aggregation_info)
def test_threshold_instance(T, N): commitments = [] # fragments[i][j] = fragment held by player i, # received from player j fragments = [[None] * N for _ in range(N)] secrets = [] # Step 1 : PrivateKey.new_threshold for player in range(N): secret_key, commi, frags = PrivateKey.new_threshold(T, N) for target, frag in enumerate(frags): fragments[target][player] = frag commitments.append(commi) secrets.append(secret_key) # Step 2 : Threshold.verify_secret_fragment for player_source in range(1, N + 1): for player_target in range(1, N + 1): assert Threshold.verify_secret_fragment( T, fragments[player_target - 1][player_source - 1], player_target, commitments[player_source - 1]) # Step 3 : master_pubkey = PublicKey.aggregate(...) # secret_share = PrivateKey.aggregate(...) master_pubkey = PublicKey.aggregate( [PublicKey.from_g1(cpoly[0].to_jacobian()) for cpoly in commitments], False) secret_shares = [ PrivateKey.aggregate(map(PrivateKey, row), None, False) for row in fragments ] master_privkey = PrivateKey.aggregate(secrets, None, False) msg = 'Test' signature_actual = master_privkey.sign(msg) # Step 4 : sig_share = secret_share.sign_threshold(...) # Check every combination of T players for X in combinations(range(1, N + 1), T): # X: a list of T indices like [1, 2, 5] # Check underlying secret key is correct r = Threshold.interpolate_at_zero( X, [secret_shares[x - 1].value for x in X]) secret_cand = PrivateKey(r) assert secret_cand == master_privkey # Check signatures signature_shares = [ secret_shares[x - 1].sign_threshold(msg, x, X) for x in X ] signature_cand = Signature.aggregate_sigs_simple(signature_shares) assert signature_cand == signature_actual # Check that the signature actually verifies the message agg_info = AggregationInfo.from_msg(master_pubkey, msg) signature_actual.set_aggregation_info(agg_info) assert signature_actual.verify() # Step 4b : Alternatively, we can add the lagrange coefficients # to 'unit' signatures. for X in combinations(range(1, N + 1), T): # X: a list of T indices like [1, 2, 5] # Check signatures signature_shares = [secret_shares[x - 1].sign(msg) for x in X] signature_cand = Threshold.aggregate_unit_sigs(signature_shares, X, T) assert signature_cand == signature_actual
def test1(): seed = bytes([ 0, 50, 6, 244, 24, 199, 1, 25, 52, 88, 192, 19, 18, 12, 89, 6, 220, 18, 102, 58, 209, 82, 12, 62, 89, 110, 182, 9, 44, 20, 254, 22 ]) sk = PrivateKey.from_seed(seed) pk = sk.get_public_key() msg = bytes([100, 2, 254, 88, 90, 45, 23]) sig = sk.sign(msg) sk_bytes = sk.serialize() pk_bytes = pk.serialize() sig_bytes = sig.serialize() sk = PrivateKey.from_bytes(sk_bytes) pk = PublicKey.from_bytes(pk_bytes) sig = Signature.from_bytes(sig_bytes) sig.set_aggregation_info(AggregationInfo.from_msg(pk, msg)) ok = sig.verify() assert (ok) seed = bytes([1]) + seed[1:] sk1 = PrivateKey.from_seed(seed) seed = bytes([2]) + seed[1:] sk2 = PrivateKey.from_seed(seed) pk1 = sk1.get_public_key() sig1 = sk1.sign(msg) pk2 = sk2.get_public_key() sig2 = sk2.sign(msg) agg_sig = Signature.aggregate([sig1, sig2]) agg_pubkey = PublicKey.aggregate([pk1, pk2]) agg_sig.set_aggregation_info(AggregationInfo.from_msg(agg_pubkey, msg)) assert (agg_sig.verify()) seed = bytes([3]) + seed[1:] sk3 = PrivateKey.from_seed(seed) pk3 = sk3.get_public_key() msg2 = bytes([100, 2, 254, 88, 90, 45, 23]) sig1 = sk1.sign(msg) sig2 = sk2.sign(msg) sig3 = sk3.sign(msg2) agg_sig_l = Signature.aggregate([sig1, sig2]) agg_sig_final = Signature.aggregate([agg_sig_l, sig3]) sig_bytes = agg_sig_final.serialize() agg_sig_final = Signature.from_bytes(sig_bytes) a1 = AggregationInfo.from_msg(pk1, msg) a2 = AggregationInfo.from_msg(pk2, msg) a3 = AggregationInfo.from_msg(pk3, msg2) a1a2 = AggregationInfo.merge_infos([a1, a2]) a_final = AggregationInfo.merge_infos([a1a2, a3]) print(a_final) agg_sig_final.set_aggregation_info(a_final) ok = agg_sig_final.verify() ok = agg_sig_l.verify() agg_sig_final = agg_sig_final.divide_by([agg_sig_l]) ok = agg_sig_final.verify() agg_sk = PrivateKey.aggregate([sk1, sk2], [pk1, pk2]) agg_sk.sign(msg) seed = bytes([ 1, 50, 6, 244, 24, 199, 1, 25, 52, 88, 192, 19, 18, 12, 89, 6, 220, 18, 102, 58, 209, 82, 12, 62, 89, 110, 182, 9, 44, 20, 254, 22 ]) esk = ExtendedPrivateKey.from_seed(seed) epk = esk.get_extended_public_key() sk_child = esk.private_child(0).private_child(5) pk_child = epk.public_child(0).public_child(5) buffer1 = pk_child.serialize() buffer2 = sk_child.serialize() print(len(buffer1), buffer1) print(len(buffer2), buffer2) assert (sk_child.get_extended_public_key() == pk_child)
def aggregate(signatures): """ Aggregates many (aggregate) signatures, using a combination of simple and secure aggregation. Signatures are grouped based on which ones share common messages, and these are all merged securely. """ public_keys = [] # List of lists message_hashes = [] # List of lists for signature in signatures: if signature.aggregation_info.empty(): raise Exception( "Each signature must have a valid aggregation " + "info") public_keys.append(signature.aggregation_info.public_keys) message_hashes.append(signature.aggregation_info.message_hashes) # Find colliding vectors, save colliding messages messages_set = set() colliding_messages_set = set() for msg_vector in message_hashes: messages_set_local = set() for msg in msg_vector: if msg in messages_set and msg not in messages_set_local: colliding_messages_set.add(msg) messages_set.add(msg) messages_set_local.add(msg) if len(colliding_messages_set) == 0: # There are no colliding messages between the groups, so we # will just aggregate them all simply. Note that we assume # that every group is a valid aggregate signature. If an invalid # or insecure signature is given, and invalid signature will # be created. We don't verify for performance reasons. final_sig = Signature.aggregate_sigs_simple(signatures) aggregation_infos = [sig.aggregation_info for sig in signatures] final_agg_info = AggregationInfo.merge_infos(aggregation_infos) final_sig.set_aggregation_info(final_agg_info) return final_sig # There are groups that share messages, therefore we need # to use a secure form of aggregation. First we find which # groups collide, and securely aggregate these. Then, we # use simple aggregation at the end. colliding_sigs = [] non_colliding_sigs = [] colliding_message_hashes = [] # List of lists colliding_public_keys = [] # List of lists for i in range(len(signatures)): group_collides = False for msg in message_hashes[i]: if msg in colliding_messages_set: group_collides = True colliding_sigs.append(signatures[i]) colliding_message_hashes.append(message_hashes[i]) colliding_public_keys.append(public_keys[i]) break if not group_collides: non_colliding_sigs.append(signatures[i]) # Arrange all signatures, sorted by their aggregation info colliding_sigs.sort(key=lambda s: s.aggregation_info) # Arrange all public keys in sorted order, by (m, pk) sort_keys_sorted = [] for i in range(len(colliding_public_keys)): for j in range(len(colliding_public_keys[i])): sort_keys_sorted.append((colliding_message_hashes[i][j], colliding_public_keys[i][j])) sort_keys_sorted.sort() sorted_public_keys = [pk for (mh, pk) in sort_keys_sorted] computed_Ts = BLS.hash_pks(len(colliding_sigs), sorted_public_keys) # Raise each sig to a power of each t, # and multiply all together into agg_sig ec = sorted_public_keys[0].value.ec agg_sig = JacobianPoint(Fq2.one(ec.q), Fq2.one(ec.q), Fq2.zero(ec.q), True, ec) for i, signature in enumerate(colliding_sigs): agg_sig += signature.value * computed_Ts[i] for signature in non_colliding_sigs: agg_sig += signature.value final_sig = Signature.from_g2(agg_sig) aggregation_infos = [sig.aggregation_info for sig in signatures] final_agg_info = AggregationInfo.merge_infos(aggregation_infos) final_sig.set_aggregation_info(final_agg_info) return final_sig