Skip to content

Commit

Permalink
Add support for decompressed points in aggregate/verfiy
Browse files Browse the repository at this point in the history
  • Loading branch information
paulmillr committed Dec 3, 2020
1 parent 757392d commit 1d0a7b6
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 44 deletions.
35 changes: 12 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ const msg = 'hello';
- [`verify(signature, hash, publicKey)`](#verifysignature-hash-publickey)
- [`aggregatePublicKeys(publicKeys)`](#aggregatepublickeyspublickeys)
- [`aggregateSignatures(signatures)`](#aggregatesignaturessignatures)
- [`verifyMultiple(hashes, publicKeys, signature)`](#verifymultiplehashes-publickeys-signature)
- [`pairing(G1Point, G2Point)`](#pairingg1point-g2point)

##### `getPublicKey(privateKey)`
Expand Down Expand Up @@ -102,9 +101,9 @@ Default domain (DST) is `BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_`, use `bls.
##### `verify(signature, hash, publicKey)`
```typescript
function verify(
signature: Uint8Array | string,
hash: Uint8Array | string,
publicKey: Uint8Array | string
signature: Uint8Array | string | PointG2,
hash: Uint8Array | string | PointG2,
publicKey: Uint8Array | string | PointG1
): Promise<boolean>
```
- `hash: Uint8Array | string` - message hash that needs to be verified
Expand All @@ -114,30 +113,17 @@ function verify(

##### `aggregatePublicKeys(publicKeys)`
```typescript
function aggregatePublicKeys(publicKeys: Uint8Array[] | string[]): Uint8Array;
function aggregatePublicKeys(publicKeys: (Uint8Array | string | PointG1)[]): Uint8Array | PointG1;
```
- `publicKeys: Uint8Array[] | string[]` - e.g. that have been generated from `privateKey` by `getPublicKey`
- Returns `Uint8Array`: one aggregated public key which calculated from public keys
- `publicKeys: (Uint8Array | string | PointG1)[]` - e.g. that have been generated from `privateKey` by `getPublicKey`
- Returns `Uint8Array | PointG1`: one aggregated public key which calculated from public keys

##### `aggregateSignatures(signatures)`
```typescript
function aggregateSignatures(signatures: Uint8Array[] | string[]): Uint8Array;
function aggregateSignatures(signatures: (Uint8Array | string | PointG2)[]): Uint8Array | PointG2;
```
- `signatures: Uint8Array[] | string[]` - e.g. that have been generated by `sign`
- Returns `Uint8Array`: one aggregated signature which calculated from signatures

##### `verifyMultiple(hashes, publicKeys, signature)`
```typescript
function verifyMultiple(
hashes: Uint8Array[] | string[],
publicKeys: Uint8Array[] | string[],
signature: Uint8Array | string
): Promise<boolean>
```
- `hashes: Uint8Array[] | string[]` - messages hashes that needs to be verified
- `publicKeys: Uint8Array[] | string[]` - e.g. that were generated from `privateKeys` by `getPublicKey`
- `signature: Uint8Array | string` - object returned by the `aggregateSignatures` function
- Returns `Promise<boolean>`: `true` / `false` whether the signature matches hashes
- `signatures: (Uint8Array | string | PointG2)[]` - e.g. that have been generated by `sign`
- Returns `Uint8Array | PointG2`: one aggregated signature which calculated from signatures

##### `pairing(G1Point, G2Point)`
```typescript
Expand Down Expand Up @@ -213,13 +199,16 @@ Benchmarks measured with 2.9Ghz i9-8950HK:
getPublicKey x 1080 ops/sec @ 925μs/op
sign x 14 ops/sec @ 70ms/op
verify x 22 ops/sec @ 44ms/op
verify (no compression) x 39 ops/sec @ 25ms/op
pairing x 52 ops/sec @ 19ms/op
aggregatePublicKeys/8 x 1033 ops/sec @ 967μs/op
aggregatePublicKeys/64 x 35 ops/sec @ 28ms/op
aggregatePublicKeys/512 x 4 ops/sec @ 227ms/op
aggregateSignatures/8 x 199 ops/sec @ 5ms/op
aggregateSignatures/64 x 6 ops/sec @ 158ms/op
aggregateSignatures/512 x 0 ops/sec @ 1320ms/op
aggregatePublicKeys/30 (no compression) x 4216 ops/sec @ 237μs/op
aggregateSignatures/30 (no compression) x 74 ops/sec @ 13ms/op
```
## Security
Expand Down
6 changes: 3 additions & 3 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export declare class PointG2 extends ProjectivePoint<Fq2> {
export declare function pairing(P: PointG1, Q: PointG2, withFinalExponent?: boolean): Fq12;
export declare function getPublicKey(privateKey: PrivateKey): Uint8Array;
export declare function sign(message: Bytes, privateKey: PrivateKey): Promise<Uint8Array>;
export declare function verify(signature: Bytes, message: Bytes, publicKey: Bytes): Promise<boolean>;
export declare function aggregatePublicKeys(publicKeys: Bytes[]): Uint8Array;
export declare function aggregateSignatures(signatures: Bytes[]): Uint8Array;
export declare function verify(signature: Bytes | PointG2, message: Bytes | PointG2, publicKey: Bytes | PointG1): Promise<boolean>;
export declare function aggregatePublicKeys(publicKeys: (Bytes | PointG1)[]): Bytes | PointG1;
export declare function aggregateSignatures(signatures: (Bytes | PointG2)[]): Bytes | PointG2;
export declare function verifyBatch(messages: Bytes[], publicKeys: Bytes[], signature: Bytes): Promise<boolean>;
14 changes: 7 additions & 7 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -330,10 +330,10 @@ async function sign(message, privateKey) {
}
exports.sign = sign;
async function verify(signature, message, publicKey) {
const P = PointG1.fromCompressedHex(publicKey).negate();
const Hm = await PointG2.hashToCurve(message);
const P = publicKey instanceof PointG1 ? publicKey : PointG1.fromCompressedHex(publicKey).negate();
const Hm = message instanceof PointG2 ? message : await PointG2.hashToCurve(message);
const G = PointG1.BASE;
const S = PointG2.fromSignature(signature);
const S = signature instanceof PointG2 ? signature : PointG2.fromSignature(signature);
const ePHm = pairing(P, Hm, false);
const eGS = pairing(G, S, false);
const exp = eGS.multiply(ePHm).finalExponentiate();
Expand All @@ -344,18 +344,18 @@ function aggregatePublicKeys(publicKeys) {
if (!publicKeys.length)
throw new Error('Expected non-empty array');
const agg = publicKeys
.map((p) => PointG1.fromCompressedHex(p))
.map((p) => p instanceof PointG1 ? p : PointG1.fromCompressedHex(p))
.reduce((sum, p) => sum.add(p), PointG1.ZERO);
return agg.toCompressedHex();
return publicKeys[0] instanceof PointG1 ? agg : agg.toCompressedHex();
}
exports.aggregatePublicKeys = aggregatePublicKeys;
function aggregateSignatures(signatures) {
if (!signatures.length)
throw new Error('Expected non-empty array');
const agg = signatures
.map((s) => PointG2.fromSignature(s))
.map((s) => s instanceof PointG2 ? s : PointG2.fromSignature(s))
.reduce((sum, s) => sum.add(s), PointG2.ZERO);
return agg.toSignature();
return signatures[0] instanceof PointG2 ? agg : agg.toSignature();
}
exports.aggregateSignatures = aggregateSignatures;
async function verifyBatch(messages, publicKeys, signature) {
Expand Down
20 changes: 10 additions & 10 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,11 +384,11 @@ export async function sign(message: Bytes, privateKey: PrivateKey): Promise<Uint
}

// e(P, H(m)) == e(G,S)
export async function verify(signature: Bytes, message: Bytes, publicKey: Bytes): Promise<boolean> {
const P = PointG1.fromCompressedHex(publicKey).negate();
const Hm = await PointG2.hashToCurve(message);
export async function verify(signature: Bytes | PointG2, message: Bytes | PointG2, publicKey: Bytes | PointG1): Promise<boolean> {
const P = publicKey instanceof PointG1 ? publicKey : PointG1.fromCompressedHex(publicKey).negate();
const Hm = message instanceof PointG2 ? message : await PointG2.hashToCurve(message);
const G = PointG1.BASE;
const S = PointG2.fromSignature(signature);
const S = signature instanceof PointG2 ? signature : PointG2.fromSignature(signature);
// Instead of doing 2 exponentiations, we use property of billinear maps
// and do one exp after multiplying 2 points.
const ePHm = pairing(P, Hm, false);
Expand All @@ -398,21 +398,21 @@ export async function verify(signature: Bytes, message: Bytes, publicKey: Bytes)
}

// pk1 + pk2 + pk3 = pkA
export function aggregatePublicKeys(publicKeys: Bytes[]): Uint8Array {
export function aggregatePublicKeys(publicKeys: (Bytes | PointG1)[]): Bytes | PointG1 {
if (!publicKeys.length) throw new Error('Expected non-empty array');
const agg = publicKeys
.map((p) => PointG1.fromCompressedHex(p))
.map((p) => p instanceof PointG1 ? p : PointG1.fromCompressedHex(p))
.reduce((sum, p) => sum.add(p), PointG1.ZERO);
return agg.toCompressedHex();
return publicKeys[0] instanceof PointG1 ? agg : agg.toCompressedHex();
}

// e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))
export function aggregateSignatures(signatures: Bytes[]): Uint8Array {
export function aggregateSignatures(signatures: (Bytes | PointG2)[]): Bytes | PointG2 {
if (!signatures.length) throw new Error('Expected non-empty array');
const agg = signatures
.map((s) => PointG2.fromSignature(s))
.map((s) => s instanceof PointG2 ? s : PointG2.fromSignature(s))
.reduce((sum, s) => sum.add(s), PointG2.ZERO);
return agg.toSignature();
return signatures[0] instanceof PointG2 ? agg : agg.toSignature();
}

export async function verifyBatch(messages: Bytes[], publicKeys: Bytes[], signature: Bytes) {
Expand Down
14 changes: 13 additions & 1 deletion test/benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,19 @@ run(async () => {
await bls.sign('09', '28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4c')
);
const pub = bls.getPublicKey('28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4c');
const pubp = bls.PointG1.fromPrivateKey('28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4c');
const msgp = await bls.PointG2.hashToCurve('09');
const sigp = bls.PointG2.fromSignature(await bls.sign('09', '28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4c'));
await mark('verify', 20, async () => {
await bls.verify(
'8647aa9680cd0cdf065b94e818ff2bb948cc97838bcee987b9bc1b76d0a0a6e0d85db4e9d75aaedfc79d4ea2733a21ae0579014de7636dd2943d45b87c82b1c66a289006b0b9767921bb8edd3f6c5c5dec0d54cd65f61513113c50cc977849e5',
'09',
pub
);
});
await mark('verify (no compression)', 20, async () => {
await bls.verify(sigp, msgp, pubp)
});
const p1 = bls.PointG1.BASE.multiply(
0x28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4cn
);
Expand All @@ -65,12 +71,18 @@ run(async () => {
//await mark('pairing (batch)', 40, () => bls.pairing(p1, p2));
await mark('pairing', 40, () => bls.pairing(p1, p2));

await mark('aggregatePublicKeys/8', 10, () => bls.aggregatePublicKeys(pubs.slice(0, 2)));
await mark('aggregatePublicKeys/8', 10, () => bls.aggregatePublicKeys(pubs.slice(0, 8)));
await mark('aggregatePublicKeys/64', 10, () => bls.aggregatePublicKeys(pubs.slice(0, 64)));
await mark('aggregatePublicKeys/512', 10, () => bls.aggregatePublicKeys(pubs.slice(0, 512)));
await mark('aggregateSignatures/8', 10, () => bls.aggregateSignatures(sigs.slice(0, 2)));
await mark('aggregateSignatures/64', 10, () => bls.aggregateSignatures(sigs.slice(0, 64)));
await mark('aggregateSignatures/512', 4, () => bls.aggregateSignatures(sigs.slice(0, 512)));

const aggp30 = pubs.slice(0, 30).map(bls.PointG1.fromCompressedHex);
await mark('aggregatePublicKeys/30 (no compression)', 10, () => bls.aggregatePublicKeys(aggp30));

const aggs30 = sigs.slice(0, 30).map(bls.PointG2.fromSignature);
await mark('aggregateSignatures/30 (no compression)', 10, () => bls.aggregatePublicKeys(aggs30));

logMem();
});

0 comments on commit 1d0a7b6

Please sign in to comment.