Source code for aggrsign_bls

'''
**BLS Multi-Signatures (BLS)**

*Authors:* Dan Boneh, Manu Drijvers, Gregory Neven

| **Title:** "BLS Multi-Signatures With Public-Key Aggregation"
| **Available from:** https://crypto.stanford.edu/~dabo/pubs/papers/BLSmultisig.html
| **Notes:** Includes both vulnerable and rogue-public-key-resistant aggregation methods

.. rubric:: Scheme Properties

* **Type:** aggregate signature
* **Setting:** bilinear groups (asymmetric)
* **Assumption:** CDH in G1

.. rubric:: Implementation

:Authors: Lovesh Harchandani
:Date: 5/2018
'''

from functools import reduce

from charm.toolbox.pairinggroup import PairingGroup, ZR, G1, G2, pair
from charm.core.engine.util import objectToBytes

debug = False


[docs] class BLSAggregation: def __init__(self, groupObj): global group group = groupObj
[docs] def keygen(self, g, secparam=None): x = group.random() g_x = g ** x pk = {'g^x': g_x, 'g': g, 'identity': str(g_x), 'secparam': secparam} sk = {'x': x} return pk, sk
[docs] def sign(self, x, message): M = self.dump(message) if debug: print("Message => '%s'" % M) return group.hash(M, G1) ** x
[docs] def verify(self, pk, sig, message): M = self.dump(message) h = group.hash(M, G1) return pair(pk['g'], sig) == pair(h, pk['g^x'])
[docs] def aggregate_sigs_vulnerable(self, signatures): """ This method of aggregation is vulnerable to rogue public key attack """ return self.product(signatures)
[docs] def verify_aggregate_sig_vulnerable(self, message, aggregate_sig, public_keys): # This method of verification is vulnerable to rogue public key attack g = self.check_and_return_same_generator_in_public_keys(public_keys) M = self.dump(message) h = group.hash(M, G1) combined_pk = self.product([pk['g^x'] for pk in public_keys]) return pair(g, aggregate_sig) == pair(combined_pk, h)
[docs] def aggregate_sigs_safe(self, pubkey_signatures): # This method of aggregation is resistant to rogue public key attack sigs = [] all_pubkeys = [i[0] for i in pubkey_signatures] for pk, sig in pubkey_signatures: e = sig ** self.hash_keys(pk, all_pubkeys) sigs.append(e) return self.product(sigs)
[docs] def verify_aggregate_sig_safe(self, message, aggregate_sig, public_keys): # This method of verification is resistant to rogue public key attack g = self.check_and_return_same_generator_in_public_keys(public_keys) aggregated_pk = self.aggregate_pub_key(public_keys) M = self.dump(message) h = group.hash(M, G1) return pair(g, aggregate_sig) == pair(aggregated_pk, h)
[docs] @staticmethod def product(seq): return reduce(lambda x, y: x * y, seq)
[docs] @staticmethod def dump(obj): return objectToBytes(obj, group)
[docs] @staticmethod def check_and_return_same_generator_in_public_keys(public_keys): gs = {pk['g'] for pk in public_keys} assert len(gs) == 1, 'All public keys should have same generator' return next(iter(gs))
[docs] @staticmethod def hash_keys(pk, all_pks): acc = BLSAggregation.dump(pk['g^x']) for p in all_pks: acc += BLSAggregation.dump(p['g^x']) return group.hash(acc, ZR)
[docs] @staticmethod def aggregate_pub_key(pks): r = [] for pk in pks: h = BLSAggregation.hash_keys(pk, pks) r.append(pk['g^x'] ** h) return BLSAggregation.product(r)
[docs] def vulnerable(): groupObj = PairingGroup('MNT224') m = {'a': "hello world!!!", 'b': "test message"} bls = BLSAggregation(groupObj) g = group.random(G2) pk1, sk1 = bls.keygen(g) pk2, sk2 = bls.keygen(g) pk3, sk3 = bls.keygen(g) sig1 = bls.sign(sk1['x'], m) sig2 = bls.sign(sk2['x'], m) sig3 = bls.sign(sk3['x'], m) if debug: print("Message: '%s'" % m) print("Signature1: '%s'" % sig1) print("Signature2: '%s'" % sig2) print("Signature3: '%s'" % sig3) assert bls.verify(pk1, sig1, m), 'Failure!!!' assert bls.verify(pk2, sig2, m), 'Failure!!!' assert bls.verify(pk3, sig3, m), 'Failure!!!' if debug: print('VERIFICATION SUCCESS!!!') aggregate_sig = bls.aggregate_sigs_vulnerable([sig1, sig2, sig3]) if debug: print("Aggregate signature: '%s'" % aggregate_sig) assert bls.verify_aggregate_sig_vulnerable(m, aggregate_sig, [pk1, pk2, pk3]), \ 'Failure!!!' if debug: print('AGGREGATION VERIFICATION SUCCESS!!!') assert not bls.verify_aggregate_sig_vulnerable(m, aggregate_sig, [pk1, pk2]) if debug: print('AGGREGATION VERIFICATION SUCCESS AGAIN!!!')
[docs] def demo_rogue_public_key_attack(): # Attack mentioned here https://crypto.stanford.edu/~dabo/pubs/papers/BLSmultisig.html groupObj = PairingGroup('MNT224') m = {'a': "hello world!!!", 'b': "test message"} bls = BLSAggregation(groupObj) g = group.random(G2) pk0, sk0 = bls.keygen(g) pk1, sk1 = bls.keygen(g) # Construct the attacker's public key (pk2) as `g^beta * (pk1*pk2)^-1`, # i.e inverse of the product of all public keys that the attacker wants # to forge the multi-sig over pk_inverse = 1 / (BLSAggregation.product([pk0['g^x'], pk1['g^x']])) beta = group.random() pk2, _ = bls.keygen(g) pk2['g^x'] = (g ** beta) * pk_inverse M = BLSAggregation.dump(m) h = group.hash(M, G1) fake_aggregate_sig = h ** beta assert bls.verify_aggregate_sig_vulnerable(m, fake_aggregate_sig, [pk0, pk1, pk2]), \ 'Failure!!!' if debug: print('ROGUE PUBLIC KEY ATTACK SUCCESS!!!')
[docs] def safe(): groupObj = PairingGroup('MNT224') m = {'a': "hello world!!!", 'b': "test message"} bls = BLSAggregation(groupObj) g = group.random(G2) pk1, sk1 = bls.keygen(g) pk2, sk2 = bls.keygen(g) pk3, sk3 = bls.keygen(g) sig1 = bls.sign(sk1['x'], m) sig2 = bls.sign(sk2['x'], m) sig3 = bls.sign(sk3['x'], m) if debug: print("Message: '%s'" % m) print("Signature1: '%s'" % sig1) print("Signature2: '%s'" % sig2) print("Signature3: '%s'" % sig3) assert bls.verify(pk1, sig1, m), 'Failure!!!' assert bls.verify(pk2, sig2, m), 'Failure!!!' assert bls.verify(pk3, sig3, m), 'Failure!!!' if debug: print('VERIFICATION SUCCESS!!!') aggregate_sig = bls.aggregate_sigs_safe([(pk1, sig1), (pk2, sig2), (pk3, sig3)]) if debug: print("Aggregate signature: '%s'" % aggregate_sig) assert bls.verify_aggregate_sig_safe(m, aggregate_sig, [pk1, pk2, pk3]), \ 'Failure!!!' if debug: print('NEW AGGREGATION VERIFICATION SUCCESS!!!') assert not bls.verify_aggregate_sig_safe(m, aggregate_sig, [pk1, pk2]) if debug: print('NEW AGGREGATION VERIFICATION SUCCESS AGAIN!!!')
[docs] def defend_rogue_public_key_attack(): # Defence mentioned here https://crypto.stanford.edu/~dabo/pubs/papers/BLSmultisig.html groupObj = PairingGroup('MNT224') m = {'a': "hello world!!!", 'b': "test message"} bls = BLSAggregation(groupObj) g = group.random(G2) pk0, sk0 = bls.keygen(g) pk1, sk1 = bls.keygen(g) # Construct the attacker's public key (pk2) as `g^beta * (pk1*pk2)^-1`, # i.e inverse of the product of all public keys that the attacker wants # to forge the multi-sig over pk_inverse = 1 / (BLSAggregation.product([pk0['g^x'], pk1['g^x']])) beta = group.random() pk2, _ = bls.keygen(g) pk2['g^x'] = (g ** beta) * pk_inverse M = BLSAggregation.dump(m) h = group.hash(M, G1) fake_aggregate_sig = h ** beta assert not bls.verify_aggregate_sig_safe(m, fake_aggregate_sig, [pk0, pk1, pk2]), \ 'Failure!!!' if debug: print('ROGUE PUBLIC KEY ATTACK DEFENDED!!!')
if __name__ == "__main__": debug = True vulnerable() demo_rogue_public_key_attack() safe() defend_rogue_public_key_attack()