Chia-Network / bls-signatures

BLS signatures in C++, using the blst library for BLS12-381
Apache License 2.0
292 stars 211 forks source link

Problem with Aggregation of Private Keys #420

Closed huseyingokay closed 12 months ago

huseyingokay commented 1 year ago

Hello,

I am able to aggregate private keys with PopSchemeMPL and I can get the same signature from the aggregated key and the aggregation of the individual signatures of the private keys. The problem is I cannot do that with AugSchemeMPL and I guess chia blockchain uses AugSchemeMPL since it doesn't accept my transactions with PopSchemeMPL when I invoke push_tx method

I would appreciate it if you could help

emlowe commented 1 year ago

Augscheme prepends the public key to the message, so this creates literally a different message to sign. Therefore you must be explicit about the public key you want to use to keep the message consistent.

For example - this doesn't work (C++ sample code), as the message all end up being different and of course then the signatures all end up different.

const vector<uint8_t> message = {100, 2, 254, 88, 90, 45, 23};
const vector<uint8_t> seed(32, 0x07);
const vector<uint8_t> seed2(32, 0x08);

const PrivateKey sk1 = AugSchemeMPL().KeyGen(seed);
const G1Element pk1 = sk1.GetG1Element();

const PrivateKey sk2 = AugSchemeMPL().KeyGen(seed2);
const G1Element pk2 = sk2.GetG1Element();

const PrivateKey aggSk = PrivateKey::Aggregate({sk1, sk2});
const G1Element aggPubKey = pk1 + pk2;

const G2Element sig1 = AugSchemeMPL().Sign(sk1, message);
const G2Element sig2 = AugSchemeMPL().Sign(sk2, message);

const G2Element aggSig2 = AugSchemeMPL().Sign(aggSk, message);

const G2Element aggSig = AugSchemeMPL().Aggregate({sig1, sig2});
REQUIRE(aggSig == aggSig2);

// Verify as a single G2Element
REQUIRE(AugSchemeMPL().Verify(aggPubKey, message, aggSig));
REQUIRE(AugSchemeMPL().Verify(aggPubKey, message, aggSig2));

The following code does work, as it uses the same public key for each "prepending"

const vector<uint8_t> message = {100, 2, 254, 88, 90, 45, 23};
const vector<uint8_t> seed(32, 0x07);
const vector<uint8_t> seed2(32, 0x08);

const PrivateKey sk1 = AugSchemeMPL().KeyGen(seed);
const G1Element pk1 = sk1.GetG1Element();

const PrivateKey sk2 = AugSchemeMPL().KeyGen(seed2);
const G1Element pk2 = sk2.GetG1Element();

const PrivateKey aggSk = PrivateKey::Aggregate({sk1, sk2});
const G1Element aggPubKey = pk1 + pk2;

const G2Element sig1 = AugSchemeMPL().Sign(sk1, message, aggPubKey);
const G2Element sig2 = AugSchemeMPL().Sign(sk2, message, aggPubKey);

// technically aggPubkey not needed, but kept for clarity
const G2Element aggSig2 = AugSchemeMPL().Sign(aggSk, message, aggPubKey);

const G2Element aggSig = AugSchemeMPL().Aggregate({sig1, sig2});
REQUIRE(aggSig == aggSig2);

// Verify as a single G2Element
REQUIRE(AugSchemeMPL().Verify(aggPubKey, message, aggSig));
REQUIRE(AugSchemeMPL().Verify(aggPubKey, message, aggSig2));

Specifically, you could use any G1Element (or PK) you want for the Sign operator, they just have to be the same, so in the above example you could also use pk1, or pk2.