add faster signature verification

This commit is contained in:
Marin Petrunić 2019-08-28 15:57:26 +02:00
parent edf107045a
commit 84bf46cb58
4 changed files with 102 additions and 22 deletions

View File

@ -5,7 +5,7 @@ import assert from "assert";
import {calculateYFlag, getModulus} from "./utils"; import {calculateYFlag, getModulus} from "./utils";
import * as random from "secure-random"; import * as random from "secure-random";
import {FP_POINT_LENGTH} from "../constants"; import {FP_POINT_LENGTH} from "../constants";
import {bytes48} from "@chainsafe/eth2.0-types"; import {BLSPubkey, bytes48} from "@chainsafe/eth2.0-types";
export class G1point { export class G1point {
@ -28,6 +28,10 @@ export class G1point {
return new G1point(sum); return new G1point(sum);
} }
public addRaW(other: bytes48): G1point {
return this.add(G1point.fromBytesCompressed(other));
}
public equal(other: G1point): boolean { public equal(other: G1point): boolean {
return this.point.equals(other.point); return this.point.equals(other.point);
} }
@ -102,6 +106,14 @@ export class G1point {
return new G1point(point); return new G1point(point);
} }
public static aggregate(values: bytes48[]): G1point {
return values.map((value) => {
return G1point.fromBytesCompressed(value);
}).reduce((previousValue, currentValue): G1point => {
return previousValue.add(currentValue);
});
}
public static generator(): G1point { public static generator(): G1point {
return new G1point(ctx.ECP.generator()); return new G1point(ctx.ECP.generator());
} }

View File

@ -58,11 +58,7 @@ export function aggregatePubkeys(publicKeys: BLSPubkey[]): BLSPubkey {
if(publicKeys.length === 0) { if(publicKeys.length === 0) {
return new G1point(new ctx.ECP()).toBytesCompressed(); return new G1point(new ctx.ECP()).toBytesCompressed();
} }
return publicKeys.map((publicKey): G1point => { return G1point.aggregate(publicKeys).toBytesCompressed();
return G1point.fromBytesCompressed(publicKey);
}).reduce((previousValue, currentValue): G1point => {
return previousValue.add(currentValue);
}).toBytesCompressed();
} }
/** /**
@ -77,6 +73,8 @@ export function verify(publicKey: BLSPubkey, messageHash: bytes32, signature: BL
const key = PublicKey.fromBytes(publicKey); const key = PublicKey.fromBytes(publicKey);
const sig = Signature.fromCompressedBytes(signature); const sig = Signature.fromCompressedBytes(signature);
key.getPoint().getPoint().affine();
sig.getPoint().getPoint().affine();
const g1Generated = G1point.generator(); const g1Generated = G1point.generator();
const e1 = ElipticCurvePairing.pair(key.getPoint(), G2point.hashToG2(messageHash, domain)); const e1 = ElipticCurvePairing.pair(key.getPoint(), G2point.hashToG2(messageHash, domain));
const e2 = ElipticCurvePairing.pair(g1Generated, sig.getPoint()); const e2 = ElipticCurvePairing.pair(g1Generated, sig.getPoint());
@ -98,20 +96,50 @@ export function verifyMultiple(publicKeys: BLSPubkey[], messageHashes: bytes32[]
return false; return false;
} }
try { try {
const g1Generated = G1point.generator(); const sig = Signature.fromCompressedBytes(signature).getPoint();
sig.getPoint().affine();
const eCombined = new ctx.FP12(1); const eCombined = new ctx.FP12(1);
publicKeys.forEach((publicKey, index): void => {
const g2 = G2point.hashToG2(messageHashes[index], domain); const reduction = messageHashes.reduce((previous, current, index) => {
if(previous.hash && current.equals(previous.hash)) {
return {
hash: previous.hash,
publicKey: previous.publicKey ?
previous.publicKey.addRaW(publicKeys[index])
:
G1point.fromBytesCompressed(publicKeys[index]),
};
} else if(!!previous.hash) {
const g2 = G2point.hashToG2(previous.hash, domain);
eCombined.mul( eCombined.mul(
ElipticCurvePairing.pair( ElipticCurvePairing.pair(
PublicKey.fromBytes(publicKey).getPoint(), previous.publicKey,
g2 g2
) )
); );
}); return {hash: current, publicKey: G1point.fromBytesCompressed(publicKeys[index])};
const e2 = ElipticCurvePairing.pair(g1Generated, Signature.fromCompressedBytes(signature).getPoint()); } else {
return {
hash: current,
publicKey: G1point.fromBytesCompressed(publicKeys[index])
};
}
}, {hash: null, publicKey: null});
const g2Final = G2point.hashToG2(reduction.hash, domain);
const keyFinal = reduction.publicKey;
eCombined.mul(
ElipticCurvePairing.pair(
keyFinal,
g2Final
)
);
const e2 = ElipticCurvePairing.pair(G1point.generator(), sig);
return e2.equals(eCombined); return e2.equals(eCombined);
} catch (e) { } catch (e) {
console.log(e);
return false; return false;
} }
} }

@ -1 +1 @@
Subproject commit 7567342c966c4e020f6ab3889f93cedb65ea9bfe Subproject commit 15a1d85125682d3ffc09a1ee9639f46c4a1653f6

View File

@ -1,6 +1,6 @@
import bls from "../../src"; import bls from "../../src";
import {Keypair} from "../../src/keypair"; import {Keypair} from "../../src/keypair";
import { sha256 } from 'js-sha256'; import {sha256} from 'js-sha256';
import {G2point} from "../../src/helpers/g2point"; import {G2point} from "../../src/helpers/g2point";
import {expect} from "chai"; import {expect} from "chai";
@ -65,7 +65,7 @@ describe('test bls', function () {
it('should fail verify signature of different message', () => { it('should fail verify signature of different message', () => {
const keypair = Keypair.generate(); const keypair = Keypair.generate();
const messageHash = Buffer.from(sha256.arrayBuffer("Test message")); const messageHash = Buffer.from(sha256.arrayBuffer("Test message"));
const messageHash2 = Buffer.from(sha256.arrayBuffer("Test message2")) const messageHash2 = Buffer.from(sha256.arrayBuffer("Test message2"));
const domain = Buffer.from("01", 'hex'); const domain = Buffer.from("01", 'hex');
const signature = keypair.privateKey.sign( const signature = keypair.privateKey.sign(
G2point.hashToG2(messageHash, domain) G2point.hashToG2(messageHash, domain)
@ -117,7 +117,7 @@ describe('test bls', function () {
describe('verify multiple', function() { describe('verify multiple', function() {
it('should verify aggregated signatures', function () { it('should verify aggregated signatures', function () {
this.timeout(5000) this.timeout(5000);
const domain = Buffer.alloc(8, 0); const domain = Buffer.alloc(8, 0);
@ -162,8 +162,48 @@ describe('test bls', function () {
expect(result).to.be.true; expect(result).to.be.true;
}); });
it('should verify aggregated signatures - same message', function () {
this.timeout(5000);
const domain = Buffer.alloc(8, 0);
const keypair1 = Keypair.generate();
const keypair2 = Keypair.generate();
const keypair3 = Keypair.generate();
const keypair4 = Keypair.generate();
const message = Buffer.from("Test1", 'utf-8');
const signature1 = keypair1.privateKey.signMessage(message, domain);
const signature2 = keypair2.privateKey.signMessage(message, domain);
const signature3 = keypair3.privateKey.signMessage(message, domain);
const signature4 = keypair4.privateKey.signMessage(message, domain);
const aggregateSignature = bls.aggregateSignatures([
signature1.toBytesCompressed(),
signature2.toBytesCompressed(),
signature3.toBytesCompressed(),
signature4.toBytesCompressed(),
]);
const result = bls.verifyMultiple(
[
keypair1.publicKey.toBytesCompressed(),
keypair2.publicKey.toBytesCompressed(),
keypair3.publicKey.toBytesCompressed(),
keypair4.publicKey.toBytesCompressed()
],
[message, message, message, message],
aggregateSignature,
domain
);
expect(result).to.be.true;
});
it('should fail to verify aggregated signatures - swapped messages', function () { it('should fail to verify aggregated signatures - swapped messages', function () {
this.timeout(5000) this.timeout(5000);
const domain = Buffer.alloc(8, 0); const domain = Buffer.alloc(8, 0);