add faster signature verification
This commit is contained in:
parent
edf107045a
commit
84bf46cb58
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
60
src/index.ts
60
src/index.ts
|
@ -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) => {
|
||||||
eCombined.mul(
|
if(previous.hash && current.equals(previous.hash)) {
|
||||||
ElipticCurvePairing.pair(
|
return {
|
||||||
PublicKey.fromBytes(publicKey).getPoint(),
|
hash: previous.hash,
|
||||||
g2
|
publicKey: previous.publicKey ?
|
||||||
)
|
previous.publicKey.addRaW(publicKeys[index])
|
||||||
);
|
:
|
||||||
});
|
G1point.fromBytesCompressed(publicKeys[index]),
|
||||||
const e2 = ElipticCurvePairing.pair(g1Generated, Signature.fromCompressedBytes(signature).getPoint());
|
};
|
||||||
|
} else if(!!previous.hash) {
|
||||||
|
const g2 = G2point.hashToG2(previous.hash, domain);
|
||||||
|
eCombined.mul(
|
||||||
|
ElipticCurvePairing.pair(
|
||||||
|
previous.publicKey,
|
||||||
|
g2
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return {hash: current, publicKey: G1point.fromBytesCompressed(publicKeys[index])};
|
||||||
|
} 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
|
|
@ -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);
|
||||||
|
|
||||||
|
|
Reference in New Issue